/*
 * This file is part of the Code::Blocks IDE and licensed under the GNU General Public License, version 3
 * http://www.gnu.org/licenses/gpl-3.0.html
 *
 * $Revision: 11216 $
 * $Id: main.cpp 11216 2017-10-23 08:22:25Z fuscated $
 * $HeadURL: http://svn.code.sf.net/p/codeblocks/code/branches/release-17.xx/src/src/main.cpp $
 */

#include <sdk.h>

#include "app.h"
#include "appglobals.h"
#include "batchbuild.h"
#include "cbauibook.h"
#include "cbstyledtextctrl.h"
#include "compilersettingsdlg.h"
#include "debuggersettingsdlg.h"
#include "dlgabout.h"
#include "dlgaboutplugin.h"
#include "environmentsettingsdlg.h"
#include "infopane.h"
#include "infowindow.h"
#include "main.h"
#include "notebookstyles.h"
#include "printdlg.h"
#include "scriptconsole.h"
#include "scriptingsettingsdlg.h"
#include "startherepage.h"
#include "switcherdlg.h"
#include "cbstatusbar.h"
#include "loggers.h"

#include <wx/display.h>
#include <wx/dnd.h>
#include <wx/fileconf.h>
#include <wx/filename.h>
#include <wx/gdicmn.h>
#include <wx/printdlg.h>
#include <wx/sstream.h>
#include <wx/tipdlg.h>
#include <wx/tokenzr.h>
#include <wx/xrc/xmlres.h>

#include <annoyingdialog.h>
#include <cbexception.h>
#include <cbplugin.h>
#include <cbproject.h>
#include <cbworkspace.h>
#include <ccmanager.h>
#include <configmanager.h>
#include <debuggermanager.h>
//#include <cbeditor.h>
#include <editorcolourset.h>
#include <editormanager.h>
#include <filefilters.h>
#include <globals.h>
#include <logmanager.h>
#include <personalitymanager.h>
#include <pluginmanager.h>
#include <projectmanager.h>
#include <scriptingmanager.h>
#include <sdk_events.h>
#include <templatemanager.h>
#include <toolsmanager.h>
#include <uservarmanager.h>
//#include <scripting/sqplus/SqPlusConst.h>
#include <scripting/sqplus/sqplus.h>
#include "debugger_interface_creator.h"
#include "debuggermenu.h"

#include "cbcolourmanager.h"
#include "editorconfigurationdlg.h"
#include "projectmanagerui.h"

class cbFileDropTarget : public wxFileDropTarget {
public:
    cbFileDropTarget(MainFrame *frame) : m_frame(frame) {}

    virtual bool OnDropFiles(wxCoord x, wxCoord y, const wxArrayString &filenames) {
        if (!m_frame) return false;
        return m_frame->OnDropFiles(x, y, filenames);
    }

private:
    MainFrame *m_frame;
};

const static wxString gDefaultLayout = wxT("缺省布局");
/*这将保持"硬编码"的默认布局*/
static wxString gDefaultLayoutData;
/*这将保留默认布局*/
static wxString gDefaultMessagePaneLayoutData;

const static wxString gMinimalLayout = wxT("最小化布局");
/*这将保持"硬编码"的默认布局*/
static wxString gMinimalLayoutData;
/*这将保留默认布局*/
static wxString gMinimalMessagePaneLayoutData;

/*
在<x/dfs.h> WxIDyFrase[x]仅存在于1...9,
所以在这里创建一个连续的编号！
wxID_FILE[X]枚举通常从5050开始到5059,
然后是wxID_OK,从5100开始.(wxWidgets 2.6、2.8和2.9版)
所以我们使用5060开始到希望在<wx/defs.h>中不会有太大变化之间的间隔
 */

enum {
    wxID_CBFILE01 = 5060, // 最近的文件...
    wxID_CBFILE02, // 5061
    wxID_CBFILE03, // 5062
    wxID_CBFILE04, // 5063
    wxID_CBFILE05, // 5064
    wxID_CBFILE06, // 5065
    wxID_CBFILE07, // 5066
    wxID_CBFILE08, // 5067
    wxID_CBFILE09, // 5068
    wxID_CBFILE10, // 5069
    wxID_CBFILE11, // 5070
    wxID_CBFILE12, // 5071
    wxID_CBFILE13, // 5072
    wxID_CBFILE14, // 5073
    wxID_CBFILE15, // 5074
    wxID_CBFILE16, // 5075
    wxID_CBFILE17, // 5076  // 从最近的项目开始...
    wxID_CBFILE18, // 5077
    wxID_CBFILE19, // 5078
    wxID_CBFILE20, // 5079
    wxID_CBFILE21, // 5080
    wxID_CBFILE22, // 5081
    wxID_CBFILE23, // 5082
    wxID_CBFILE24, // 5083
    wxID_CBFILE25, // 5084
    wxID_CBFILE26, // 5085
    wxID_CBFILE27, // 5086
    wxID_CBFILE28, // 5087
    wxID_CBFILE29, // 5088
    wxID_CBFILE30, // 5089
    wxID_CBFILE31, // 5090
    wxID_CBFILE32  // 5091
};
/*XRCID宏来自 wx->xrc->xmlres的操作 #define XRCID(str_id) wxXmlResource::DoGetXRCID(str_id)*/

int idToolNew = XRCID("idToolNew");
int idFileNew = XRCID("idFileNew");
int idFileNewEmpty = XRCID("idFileNewEmpty");
int idFileNewProject = XRCID("idFileNewProject");
int idFileNewTarget = XRCID("idFileNewTarget");
int idFileNewFile = XRCID("idFileNewFile");
int idFileNewCustom = XRCID("idFileNewCustom");
int idFileNewUser = XRCID("idFileNewUser");
int idFileOpen = XRCID("idFileOpen");
int idFileReopen = XRCID("idFileReopen");
int idFileOpenRecentFileClearHistory = XRCID("idFileOpenRecentFileClearHistory");
int idFileOpenRecentProjectClearHistory = XRCID("idFileOpenRecentProjectClearHistory");
int idFileImportProjectDevCpp = XRCID("idFileImportProjectDevCpp");
int idFileImportProjectMSVC = XRCID("idFileImportProjectMSVC");
int idFileImportProjectMSVCWksp = XRCID("idFileImportProjectMSVCWksp");
int idFileImportProjectMSVS = XRCID("idFileImportProjectMSVS");
int idFileImportProjectMSVSWksp = XRCID("idFileImportProjectMSVSWksp");
int idFileSave = XRCID("idFileSave");
int idFileSaveAs = XRCID("idFileSaveAs");
int idFileReopenProject = XRCID("idFileReopenProject");
int idFileSaveProject = XRCID("idFileSaveProject");
int idFileSaveProjectAs = XRCID("idFileSaveProjectAs");
int idFileSaveProjectTemplate = XRCID("idFileSaveProjectTemplate");
int idFileOpenDefWorkspace = XRCID("idFileOpenDefWorkspace");
int idFileSaveWorkspace = XRCID("idFileSaveWorkspace");
int idFileSaveWorkspaceAs = XRCID("idFileSaveWorkspaceAs");
int idFileSaveAll = XRCID("idFileSaveAll");
int idFileCloseWorkspace = XRCID("idFileCloseWorkspace");
int idFileClose = XRCID("idFileClose");
int idFileCloseAll = XRCID("idFileCloseAll");
int idFileCloseProject = XRCID("idFileCloseProject");
int idFilePrintSetup = XRCID("idFilePrintSetup");
int idFilePrint = XRCID("idFilePrint");
int idFileExit = XRCID("idFileExit");

int idEditUndo = XRCID("idEditUndo");
int idEditRedo = XRCID("idEditRedo");
int idEditClearHistory = XRCID("idEditClearHistory");
int idEditCopy = XRCID("idEditCopy");
int idEditCut = XRCID("idEditCut");
int idEditPaste = XRCID("idEditPaste");
int idEditSwapHeaderSource = XRCID("idEditSwapHeaderSource");
int idEditGotoMatchingBrace = XRCID("idEditGotoMatchingBrace");
int idEditHighlightMode = XRCID("idEditHighlightMode");
int idEditHighlightModeText = XRCID("idEditHighlightModeText");
int idEditBookmarks = XRCID("idEditBookmarks");
int idEditBookmarksToggle = XRCID("idEditBookmarksToggle");
int idEditBookmarksPrevious = XRCID("idEditBookmarksPrevious");
int idEditBookmarksNext = XRCID("idEditBookmarksNext");
int idEditBookmarksClearAll = XRCID("idEditBookmarksClearAll");
int idEditFolding = XRCID("idEditFolding");
int idEditFoldAll = XRCID("idEditFoldAll");
int idEditUnfoldAll = XRCID("idEditUnfoldAll");
int idEditToggleAllFolds = XRCID("idEditToggleAllFolds");
int idEditFoldBlock = XRCID("idEditFoldBlock");
int idEditUnfoldBlock = XRCID("idEditUnfoldBlock");
int idEditToggleFoldBlock = XRCID("idEditToggleFoldBlock");
int idEditEOLMode = XRCID("idEditEOLMode");
int idEditEOLCRLF = XRCID("idEditEOLCRLF");
int idEditEOLCR = XRCID("idEditEOLCR");
int idEditEOLLF = XRCID("idEditEOLLF");
int idEditEncoding = XRCID("idEditEncoding");
int idEditEncodingDefault = XRCID("idEditEncodingDefault");
int idEditEncodingUseBom = XRCID("idEditEncodingUseBom");
int idEditEncodingAscii = XRCID("idEditEncodingAscii");
int idEditEncodingUtf7 = XRCID("idEditEncodingUtf7");
int idEditEncodingUtf8 = XRCID("idEditEncodingUtf8");
int idEditEncodingUnicode = XRCID("idEditEncodingUnicode");
int idEditEncodingUtf16 = XRCID("idEditEncodingUtf16");
int idEditEncodingUtf32 = XRCID("idEditEncodingUtf32");
int idEditEncodingUnicode16BE = XRCID("idEditEncodingUnicode16BE");
int idEditEncodingUnicode16LE = XRCID("idEditEncodingUnicode16LE");
int idEditEncodingUnicode32BE = XRCID("idEditEncodingUnicode32BE");
int idEditEncodingUnicode32LE = XRCID("idEditEncodingUnicode32LE");
int idEditSpecialCommands = XRCID("idEditSpecialCommands");
int idEditSpecialCommandsMovement = XRCID("idEditSpecialCommandsMovement");
int idEditParaUp = XRCID("idEditParaUp");
int idEditParaUpExtend = XRCID("idEditParaUpExtend");
int idEditParaDown = XRCID("idEditParaDown");
int idEditParaDownExtend = XRCID("idEditParaDownExtend");
int idEditWordPartLeft = XRCID("idEditWordPartLeft");
int idEditWordPartLeftExtend = XRCID("idEditWordPartLeftExtend");
int idEditWordPartRight = XRCID("idEditWordPartRight");
int idEditWordPartRightExtend = XRCID("idEditWordPartRightExtend");
int idEditSpecialCommandsZoom = XRCID("idEditSpecialCommandsZoom");
int idEditZoomIn = XRCID("idEditZoomIn");
int idEditZoomOut = XRCID("idEditZoomOut");
int idEditZoomReset = XRCID("idEditZoomReset");
int idEditSpecialCommandsLine = XRCID("idEditSpecialCommandsLine");
int idEditLineCut = XRCID("idEditLineCut");
int idEditLineDelete = XRCID("idEditLineDelete");
int idEditLineDuplicate = XRCID("idEditLineDuplicate");
int idEditLineTranspose = XRCID("idEditLineTranspose");
int idEditLineCopy = XRCID("idEditLineCopy");
int idEditLinePaste = XRCID("idEditLinePaste");
int idEditLineUp = XRCID("idEditLineUp");
int idEditLineDown = XRCID("idEditLineDown");
int idEditSpecialCommandsCase = XRCID("idEditSpecialCommandsCase");
int idEditUpperCase = XRCID("idEditUpperCase");
int idEditLowerCase = XRCID("idEditLowerCase");
int idEditSpecialCommandsOther = XRCID("idEditSpecialCommandsOther");
int idEditInsertNewLine = XRCID("idEditInsertNewLine");
int idEditGotoLineEnd = XRCID("idEditGotoLineEnd");
int idEditInsertNewLineBelow = XRCID("idEditInsertNewLineBelow");
int idEditInsertNewLineAbove = XRCID("idEditInsertNewLineAbove");
int idEditSelectAll = XRCID("idEditSelectAll");
int idEditSelectNext = XRCID("idEditSelectNext");
int idEditSelectNextSkip = XRCID("idEditSelectNextSkip");
int idEditCommentSelected = XRCID("idEditCommentSelected");
int idEditUncommentSelected = XRCID("idEditUncommentSelected");
int idEditToggleCommentSelected = XRCID("idEditToggleCommentSelected");
int idEditStreamCommentSelected = XRCID("idEditStreamCommentSelected");
int idEditBoxCommentSelected = XRCID("idEditBoxCommentSelected");
int idEditShowCallTip = XRCID("idEditShowCallTip");
int idEditCompleteCode = wxNewId();

int idViewLayoutDelete = XRCID("idViewLayoutDelete");
int idViewLayoutSave = XRCID("idViewLayoutSave");
int idViewToolbars = XRCID("idViewToolbars");
int idViewToolFit = XRCID("idViewToolFit");
int idViewToolOptimize = XRCID("idViewToolOptimize");
int idViewToolMain = XRCID("idViewToolMain");
int idViewToolDebugger = XRCID("idViewToolDebugger");
int idViewManager = XRCID("idViewManager");
int idViewLogManager = XRCID("idViewLogManager");
int idViewStartPage = XRCID("idViewStartPage");
int idViewStatusbar = XRCID("idViewStatusbar");
int idViewScriptConsole = XRCID("idViewScriptConsole");
int idViewHideEditorTabs = XRCID("idViewHideEditorTabs");
int idViewFocusEditor = XRCID("idViewFocusEditor");
int idViewFocusManagement = XRCID("idViewFocusManagement");
int idViewFocusLogsAndOthers = XRCID("idViewFocusLogsAndOthers");
int idViewSwitchTabs = XRCID("idViewSwitchTabs");
int idViewFullScreen = XRCID("idViewFullScreen");

int idSearchFind = XRCID("idSearchFind");
int idSearchFindInFiles = XRCID("idSearchFindInFiles");
int idSearchFindNext = XRCID("idSearchFindNext");
int idSearchFindPrevious = XRCID("idSearchFindPrevious");
int idSearchFindSelectedNext = XRCID("idSearchFindSelectedNext");
int idSearchFindSelectedPrevious = XRCID("idSearchFindSelectedPrevious");
int idSearchReplace = XRCID("idSearchReplace");
int idSearchReplaceInFiles = XRCID("idSearchReplaceInFiles");
int idSearchGotoLine = XRCID("idSearchGotoLine");
int idSearchGotoNextChanged = XRCID("idSearchGotoNextChanged");
int idSearchGotoPreviousChanged = XRCID("idSearchGotoPreviousChanged");

int idSettingsEnvironment = XRCID("idSettingsEnvironment");
int idSettingsGlobalUserVars = XRCID("idSettingsGlobalUserVars");
int idSettingsEditor = XRCID("idSettingsEditor");
int idSettingsCompiler = XRCID("idSettingsCompiler");
int idSettingsDebugger = XRCID("idSettingsDebugger");
int idPluginsManagePlugins = XRCID("idPluginsManagePlugins");
int idSettingsScripting = XRCID("idSettingsScripting");

int idHelpTips = XRCID("idHelpTips");
int idHelpPlugins = XRCID("idHelpPlugins");

int idLeftSash = XRCID("idLeftSash");
int idBottomSash = XRCID("idBottomSash");
int idCloseFullScreen = XRCID("idCloseFullScreen");

int idFileNext = wxNewId();
int idFilePrev = wxNewId();
int idShiftTab = wxNewId();
int idCtrlAltTab = wxNewId();
int idStartHerePageLink = wxNewId();
int idHighlightButton = wxNewId();

BEGIN_EVENT_TABLE(MainFrame, wxFrame)
                EVT_ERASE_BACKGROUND(MainFrame::OnEraseBackground)
                EVT_SIZE(MainFrame::OnSize)
                EVT_CLOSE(MainFrame::OnApplicationClose)

                EVT_UPDATE_UI(idFileOpen, MainFrame::OnFileMenuUpdateUI)
                EVT_UPDATE_UI(idFileOpenRecentFileClearHistory, MainFrame::OnFileMenuUpdateUI)
                EVT_UPDATE_UI(idFileOpenRecentProjectClearHistory, MainFrame::OnFileMenuUpdateUI)
                EVT_UPDATE_UI(idFileSave, MainFrame::OnFileMenuUpdateUI)
                EVT_UPDATE_UI(idFileSaveAs, MainFrame::OnFileMenuUpdateUI)
                EVT_UPDATE_UI(idFileOpenDefWorkspace, MainFrame::OnFileMenuUpdateUI)
                EVT_UPDATE_UI(idFileSaveWorkspace, MainFrame::OnFileMenuUpdateUI)
                EVT_UPDATE_UI(idFileSaveWorkspaceAs, MainFrame::OnFileMenuUpdateUI)
                EVT_UPDATE_UI(idFileCloseWorkspace, MainFrame::OnFileMenuUpdateUI)
                EVT_UPDATE_UI(idFileClose, MainFrame::OnFileMenuUpdateUI)
                EVT_UPDATE_UI(idFileCloseAll, MainFrame::OnFileMenuUpdateUI)
                EVT_UPDATE_UI(idFilePrintSetup, MainFrame::OnFileMenuUpdateUI)
                EVT_UPDATE_UI(idFilePrint, MainFrame::OnFileMenuUpdateUI)

                EVT_UPDATE_UI(idFileReopenProject, MainFrame::OnFileMenuUpdateUI)
                EVT_UPDATE_UI(idFileSaveProject, MainFrame::OnFileMenuUpdateUI)
                EVT_UPDATE_UI(idFileSaveProjectAs, MainFrame::OnFileMenuUpdateUI)
                EVT_UPDATE_UI(idFileSaveProjectTemplate, MainFrame::OnFileMenuUpdateUI)
                EVT_UPDATE_UI(idFileSaveAll, MainFrame::OnFileMenuUpdateUI)
                EVT_UPDATE_UI(idFileCloseProject, MainFrame::OnFileMenuUpdateUI)

                EVT_UPDATE_UI(idEditUndo, MainFrame::OnEditMenuUpdateUI)
                EVT_UPDATE_UI(idEditRedo, MainFrame::OnEditMenuUpdateUI)
                EVT_UPDATE_UI(idEditClearHistory, MainFrame::OnEditMenuUpdateUI)
                EVT_UPDATE_UI(idEditCopy, MainFrame::OnEditMenuUpdateUI)
                EVT_UPDATE_UI(idEditCut, MainFrame::OnEditMenuUpdateUI)
                EVT_UPDATE_UI(idEditPaste, MainFrame::OnEditMenuUpdateUI)
                EVT_UPDATE_UI(idEditSwapHeaderSource, MainFrame::OnEditMenuUpdateUI)
                EVT_UPDATE_UI(idEditGotoMatchingBrace, MainFrame::OnEditMenuUpdateUI)
                EVT_UPDATE_UI(idEditFoldAll, MainFrame::OnEditMenuUpdateUI)
                EVT_UPDATE_UI(idEditUnfoldAll, MainFrame::OnEditMenuUpdateUI)
                EVT_UPDATE_UI(idEditToggleAllFolds, MainFrame::OnEditMenuUpdateUI)
                EVT_UPDATE_UI(idEditFoldBlock, MainFrame::OnEditMenuUpdateUI)
                EVT_UPDATE_UI(idEditUnfoldBlock, MainFrame::OnEditMenuUpdateUI)
                EVT_UPDATE_UI(idEditToggleFoldBlock, MainFrame::OnEditMenuUpdateUI)
                EVT_UPDATE_UI(idEditEOLCRLF, MainFrame::OnEditMenuUpdateUI)
                EVT_UPDATE_UI(idEditEOLCR, MainFrame::OnEditMenuUpdateUI)
                EVT_UPDATE_UI(idEditEOLLF, MainFrame::OnEditMenuUpdateUI)
                EVT_UPDATE_UI(idEditEncoding, MainFrame::OnEditMenuUpdateUI)
                EVT_UPDATE_UI(idEditSelectAll, MainFrame::OnEditMenuUpdateUI)
                EVT_UPDATE_UI(idEditSelectNext, MainFrame::OnEditMenuUpdateUI)
                EVT_UPDATE_UI(idEditSelectNextSkip, MainFrame::OnEditMenuUpdateUI)
                EVT_UPDATE_UI(idEditBookmarksToggle, MainFrame::OnEditMenuUpdateUI)
                EVT_UPDATE_UI(idEditBookmarksNext, MainFrame::OnEditMenuUpdateUI)
                EVT_UPDATE_UI(idEditBookmarksPrevious, MainFrame::OnEditMenuUpdateUI)
                EVT_UPDATE_UI(idEditBookmarksClearAll, MainFrame::OnEditMenuUpdateUI)
                EVT_UPDATE_UI(idEditCommentSelected, MainFrame::OnEditMenuUpdateUI)
                EVT_UPDATE_UI(idEditUncommentSelected, MainFrame::OnEditMenuUpdateUI)
                EVT_UPDATE_UI(idEditToggleCommentSelected, MainFrame::OnEditMenuUpdateUI)
                EVT_UPDATE_UI(idEditStreamCommentSelected, MainFrame::OnEditMenuUpdateUI)
                EVT_UPDATE_UI(idEditBoxCommentSelected, MainFrame::OnEditMenuUpdateUI)
                EVT_UPDATE_UI(idEditShowCallTip, MainFrame::OnEditMenuUpdateUI)
                EVT_UPDATE_UI(idEditCompleteCode, MainFrame::OnEditMenuUpdateUI)

                EVT_UPDATE_UI(idSearchFind, MainFrame::OnSearchMenuUpdateUI)
                EVT_UPDATE_UI(idSearchFindInFiles, MainFrame::OnSearchMenuUpdateUI)
                EVT_UPDATE_UI(idSearchFindNext, MainFrame::OnSearchMenuUpdateUI)
                EVT_UPDATE_UI(idSearchFindPrevious, MainFrame::OnSearchMenuUpdateUI)
                EVT_UPDATE_UI(idSearchFindSelectedNext, MainFrame::OnSearchMenuUpdateUI)
                EVT_UPDATE_UI(idSearchFindSelectedPrevious, MainFrame::OnSearchMenuUpdateUI)
                EVT_UPDATE_UI(idSearchReplace, MainFrame::OnSearchMenuUpdateUI)
                EVT_UPDATE_UI(idSearchReplaceInFiles, MainFrame::OnSearchMenuUpdateUI)
                EVT_UPDATE_UI(idSearchGotoLine, MainFrame::OnSearchMenuUpdateUI)
                EVT_UPDATE_UI(idSearchGotoNextChanged, MainFrame::OnSearchMenuUpdateUI)
                EVT_UPDATE_UI(idSearchGotoPreviousChanged, MainFrame::OnSearchMenuUpdateUI)

                EVT_UPDATE_UI(idViewToolMain, MainFrame::OnViewMenuUpdateUI)
                EVT_UPDATE_UI(idViewLogManager, MainFrame::OnViewMenuUpdateUI)
                EVT_UPDATE_UI(idViewStartPage, MainFrame::OnViewMenuUpdateUI)
                EVT_UPDATE_UI(idViewManager, MainFrame::OnViewMenuUpdateUI)
                EVT_UPDATE_UI(idViewStatusbar, MainFrame::OnViewMenuUpdateUI)
                EVT_UPDATE_UI(idViewScriptConsole, MainFrame::OnViewMenuUpdateUI)
                EVT_UPDATE_UI(idViewHideEditorTabs, MainFrame::OnViewMenuUpdateUI)
                EVT_UPDATE_UI(idViewFocusEditor, MainFrame::OnViewMenuUpdateUI)
                EVT_UPDATE_UI(idViewFocusManagement, MainFrame::OnViewMenuUpdateUI)
                EVT_UPDATE_UI(idViewFocusLogsAndOthers, MainFrame::OnViewMenuUpdateUI)
                EVT_UPDATE_UI(idViewFullScreen, MainFrame::OnViewMenuUpdateUI)

                EVT_MENU(idFileNewEmpty, MainFrame::OnFileNewWhat)
                EVT_MENU(idFileNewProject, MainFrame::OnFileNewWhat)
                EVT_MENU(idFileNewTarget, MainFrame::OnFileNewWhat)
                EVT_MENU(idFileNewFile, MainFrame::OnFileNewWhat)
                EVT_MENU(idFileNewCustom, MainFrame::OnFileNewWhat)
                EVT_MENU(idFileNewUser, MainFrame::OnFileNewWhat)

                EVT_MENU(idToolNew, MainFrame::OnFileNew)
                EVT_MENU(idFileOpen, MainFrame::OnFileOpen)
                EVT_MENU(idFileOpenRecentProjectClearHistory, MainFrame::OnFileOpenRecentProjectClearHistory)
                EVT_MENU(idFileOpenRecentFileClearHistory, MainFrame::OnFileOpenRecentClearHistory)
                EVT_MENU_RANGE(wxID_CBFILE01, wxID_CBFILE16, MainFrame::OnFileReopen)
                EVT_MENU_RANGE(wxID_CBFILE17, wxID_CBFILE32, MainFrame::OnFileReopenProject)
                EVT_MENU(idFileImportProjectDevCpp, MainFrame::OnFileImportProjectDevCpp)
                EVT_MENU(idFileImportProjectMSVC, MainFrame::OnFileImportProjectMSVC)
                EVT_MENU(idFileImportProjectMSVCWksp, MainFrame::OnFileImportProjectMSVCWksp)
                EVT_MENU(idFileImportProjectMSVS, MainFrame::OnFileImportProjectMSVS)
                EVT_MENU(idFileImportProjectMSVSWksp, MainFrame::OnFileImportProjectMSVSWksp)
                EVT_MENU(idFileSave, MainFrame::OnFileSave)
                EVT_MENU(idFileSaveAs, MainFrame::OnFileSaveAs)
                EVT_MENU(idFileSaveProject, MainFrame::OnFileSaveProject)
                EVT_MENU(idFileSaveProjectAs, MainFrame::OnFileSaveProjectAs)
                EVT_MENU(idFileSaveProjectTemplate, MainFrame::OnFileSaveProjectTemplate)
                EVT_MENU(idFileOpenDefWorkspace, MainFrame::OnFileOpenDefWorkspace)
                EVT_MENU(idFileSaveWorkspace, MainFrame::OnFileSaveWorkspace)
                EVT_MENU(idFileSaveWorkspaceAs, MainFrame::OnFileSaveWorkspaceAs)
                EVT_MENU(idFileSaveAll, MainFrame::OnFileSaveAll)
                EVT_MENU(idFileCloseWorkspace, MainFrame::OnFileCloseWorkspace)
                EVT_MENU(idFileClose, MainFrame::OnFileClose)
                EVT_MENU(idFileCloseAll, MainFrame::OnFileCloseAll)
                EVT_MENU(idFileCloseProject, MainFrame::OnFileCloseProject)
                EVT_MENU(idFilePrint, MainFrame::OnFilePrint)
                EVT_MENU(idFileExit, MainFrame::OnFileQuit)
                EVT_MENU(idFileNext, MainFrame::OnFileNext)
                EVT_MENU(idFilePrev, MainFrame::OnFilePrev)

                EVT_MENU(idEditUndo, MainFrame::OnEditUndo)
                EVT_MENU(idEditRedo, MainFrame::OnEditRedo)
                EVT_MENU(idEditClearHistory, MainFrame::OnEditClearHistory)
                EVT_MENU(idEditCopy, MainFrame::OnEditCopy)
                EVT_MENU(idEditCut, MainFrame::OnEditCut)
                EVT_MENU(idEditPaste, MainFrame::OnEditPaste)
                EVT_MENU(idEditSwapHeaderSource, MainFrame::OnEditSwapHeaderSource)
                EVT_MENU(idEditGotoMatchingBrace, MainFrame::OnEditGotoMatchingBrace)
                EVT_MENU(idEditHighlightModeText, MainFrame::OnEditHighlightMode)
                EVT_MENU(idEditFoldAll, MainFrame::OnEditFoldAll)
                EVT_MENU(idEditUnfoldAll, MainFrame::OnEditUnfoldAll)
                EVT_MENU(idEditToggleAllFolds, MainFrame::OnEditToggleAllFolds)
                EVT_MENU(idEditFoldBlock, MainFrame::OnEditFoldBlock)
                EVT_MENU(idEditUnfoldBlock, MainFrame::OnEditUnfoldBlock)
                EVT_MENU(idEditToggleFoldBlock, MainFrame::OnEditToggleFoldBlock)
                EVT_MENU(idEditEOLCRLF, MainFrame::OnEditEOLMode)
                EVT_MENU(idEditEOLCR, MainFrame::OnEditEOLMode)
                EVT_MENU(idEditEOLLF, MainFrame::OnEditEOLMode)
                EVT_MENU(idEditEncodingDefault, MainFrame::OnEditEncoding)
                EVT_MENU(idEditEncodingUseBom, MainFrame::OnEditEncoding)
                EVT_MENU(idEditEncodingAscii, MainFrame::OnEditEncoding)
                EVT_MENU(idEditEncodingUtf7, MainFrame::OnEditEncoding)
                EVT_MENU(idEditEncodingUtf8, MainFrame::OnEditEncoding)
                EVT_MENU(idEditEncodingUnicode, MainFrame::OnEditEncoding)
                EVT_MENU(idEditEncodingUtf16, MainFrame::OnEditEncoding)
                EVT_MENU(idEditEncodingUtf32, MainFrame::OnEditEncoding)
                EVT_MENU(idEditEncodingUnicode16BE, MainFrame::OnEditEncoding)
                EVT_MENU(idEditEncodingUnicode16LE, MainFrame::OnEditEncoding)
                EVT_MENU(idEditEncodingUnicode32BE, MainFrame::OnEditEncoding)
                EVT_MENU(idEditEncodingUnicode32LE, MainFrame::OnEditEncoding)
                EVT_MENU(idEditParaUp, MainFrame::OnEditParaUp)
                EVT_MENU(idEditParaUpExtend, MainFrame::OnEditParaUpExtend)
                EVT_MENU(idEditParaDown, MainFrame::OnEditParaDown)
                EVT_MENU(idEditParaDownExtend, MainFrame::OnEditParaDownExtend)
                EVT_MENU(idEditWordPartLeft, MainFrame::OnEditWordPartLeft)
                EVT_MENU(idEditWordPartLeftExtend, MainFrame::OnEditWordPartLeftExtend)
                EVT_MENU(idEditWordPartRight, MainFrame::OnEditWordPartRight)
                EVT_MENU(idEditWordPartRightExtend, MainFrame::OnEditWordPartRightExtend)
                EVT_MENU(idEditZoomIn, MainFrame::OnEditZoomIn)
                EVT_MENU(idEditZoomOut, MainFrame::OnEditZoomOut)
                EVT_MENU(idEditZoomReset, MainFrame::OnEditZoomReset)
                EVT_MENU(idEditLineCut, MainFrame::OnEditLineCut)
                EVT_MENU(idEditLineDelete, MainFrame::OnEditLineDelete)
                EVT_MENU(idEditLineDuplicate, MainFrame::OnEditLineDuplicate)
                EVT_MENU(idEditLineTranspose, MainFrame::OnEditLineTranspose)
                EVT_MENU(idEditLineCopy, MainFrame::OnEditLineCopy)
                EVT_MENU(idEditLinePaste, MainFrame::OnEditLinePaste)
                EVT_MENU(idEditLineUp, MainFrame::OnEditLineMove)
                EVT_MENU(idEditLineDown, MainFrame::OnEditLineMove)
                EVT_MENU(idEditUpperCase, MainFrame::OnEditUpperCase)
                EVT_MENU(idEditLowerCase, MainFrame::OnEditLowerCase)
                EVT_MENU(idEditInsertNewLine, MainFrame::OnEditInsertNewLine)
                EVT_MENU(idEditGotoLineEnd, MainFrame::OnEditGotoLineEnd)
                EVT_MENU(idEditInsertNewLineBelow, MainFrame::OnEditInsertNewLineBelow)
                EVT_MENU(idEditInsertNewLineAbove, MainFrame::OnEditInsertNewLineAbove)
                EVT_MENU(idEditSelectAll, MainFrame::OnEditSelectAll)
                EVT_MENU(idEditSelectNext, MainFrame::OnEditSelectNext)
                EVT_MENU(idEditSelectNextSkip, MainFrame::OnEditSelectNextSkip)
                EVT_MENU(idEditBookmarksToggle, MainFrame::OnEditBookmarksToggle)
                EVT_MENU(idEditBookmarksNext, MainFrame::OnEditBookmarksNext)
                EVT_MENU(idEditBookmarksPrevious, MainFrame::OnEditBookmarksPrevious)
                EVT_MENU(idEditBookmarksClearAll, MainFrame::OnEditBookmarksClearAll)
                EVT_MENU(idEditCommentSelected, MainFrame::OnEditCommentSelected)
                EVT_MENU(idEditUncommentSelected, MainFrame::OnEditUncommentSelected)
                EVT_MENU(idEditToggleCommentSelected, MainFrame::OnEditToggleCommentSelected)
                EVT_MENU(idEditStreamCommentSelected, MainFrame::OnEditStreamCommentSelected)
                EVT_MENU(idEditBoxCommentSelected, MainFrame::OnEditBoxCommentSelected)
                EVT_MENU(idEditShowCallTip, MainFrame::OnEditShowCallTip)
                EVT_MENU(idEditCompleteCode, MainFrame::OnEditCompleteCode)

                EVT_MENU(idSearchFind, MainFrame::OnSearchFind)
                EVT_MENU(idSearchFindInFiles, MainFrame::OnSearchFind)
                EVT_MENU(idSearchFindNext, MainFrame::OnSearchFindNext)
                EVT_MENU(idSearchFindPrevious, MainFrame::OnSearchFindNext)
                EVT_MENU(idSearchFindSelectedNext, MainFrame::OnSearchFindNextSelected)
                EVT_MENU(idSearchFindSelectedPrevious, MainFrame::OnSearchFindNextSelected)
                EVT_MENU(idSearchReplace, MainFrame::OnSearchReplace)
                EVT_MENU(idSearchReplaceInFiles, MainFrame::OnSearchReplace)
                EVT_MENU(idSearchGotoLine, MainFrame::OnSearchGotoLine)
                EVT_MENU(idSearchGotoNextChanged, MainFrame::OnSearchGotoNextChanged)
                EVT_MENU(idSearchGotoPreviousChanged, MainFrame::OnSearchGotoPrevChanged)

                EVT_MENU(idViewLayoutSave, MainFrame::OnViewLayoutSave)
                EVT_MENU(idViewLayoutDelete, MainFrame::OnViewLayoutDelete)
                EVT_MENU(idViewToolFit, MainFrame::OnViewToolbarsFit)
                EVT_MENU(idViewToolOptimize, MainFrame::OnViewToolbarsOptimize)
                EVT_MENU(idViewToolMain, MainFrame::OnToggleBar)
                EVT_MENU(idViewToolDebugger, MainFrame::OnToggleBar)
                EVT_MENU(idViewLogManager, MainFrame::OnToggleBar)
                EVT_MENU(idViewManager, MainFrame::OnToggleBar)
                EVT_MENU(idViewStatusbar, MainFrame::OnToggleStatusBar)
                EVT_MENU(idViewScriptConsole, MainFrame::OnViewScriptConsole)
                EVT_MENU(idViewHideEditorTabs, MainFrame::OnViewHideEditorTabs)
                EVT_MENU(idViewFocusEditor, MainFrame::OnFocusEditor)
                EVT_MENU(idViewFocusManagement, MainFrame::OnFocusManagement)
                EVT_MENU(idViewFocusLogsAndOthers, MainFrame::OnFocusLogsAndOthers)
                EVT_MENU(idViewSwitchTabs, MainFrame::OnSwitchTabs)
                EVT_MENU(idViewFullScreen, MainFrame::OnToggleFullScreen)
                EVT_MENU(idViewStartPage, MainFrame::OnToggleStartPage)

                EVT_MENU(idSettingsEnvironment, MainFrame::OnSettingsEnvironment)
                EVT_MENU(idSettingsGlobalUserVars, MainFrame::OnGlobalUserVars)
                EVT_MENU(idSettingsEditor, MainFrame::OnSettingsEditor)
                EVT_MENU(idSettingsCompiler, MainFrame::OnSettingsCompiler)
                EVT_MENU(idSettingsDebugger, MainFrame::OnSettingsDebugger)
                EVT_MENU(idPluginsManagePlugins, MainFrame::OnSettingsPlugins)
                EVT_MENU(idSettingsScripting, MainFrame::OnSettingsScripting)

                EVT_MENU(wxID_ABOUT, MainFrame::OnHelpAbout)
                EVT_MENU(idHelpTips, MainFrame::OnHelpTips)

                EVT_MENU(idStartHerePageLink, MainFrame::OnStartHereLink)

                EVT_CBAUIBOOK_LEFT_DCLICK(ID_NBEditorManager, MainFrame::OnNotebookDoubleClick)
                EVT_NOTEBOOK_PAGE_CHANGED(ID_NBEditorManager, MainFrame::OnPageChanged)

                /*按钮高亮*/
                EVT_BUTTON(idHighlightButton, MainFrame::OnHighlightMenu)
                /*处理CloseFullScreen事件*/
                EVT_BUTTON(idCloseFullScreen, MainFrame::OnToggleFullScreen)

                /*Shift-Tab 错误解决方法*/
                EVT_MENU(idShiftTab, MainFrame::OnShiftTab)
                EVT_MENU(idCtrlAltTab, MainFrame::OnCtrlAltTab)

                /*Used for mouse right click in the free area of MainFrame*/
                /*用于在MainFrame的自由区域中单击鼠标右键*/
                EVT_RIGHT_UP(MainFrame::OnMouseRightUp)

END_EVENT_TABLE()

MainFrame::MainFrame(wxWindow *parent)
        : wxFrame(parent, -1, _T("MainWin"), wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_STYLE | wxNO_FULL_REPAINT_ON_RESIZE),
          m_LayoutManager(this),
          m_pAccel(nullptr),
          m_filesHistory(wxT("文件"), wxT("/recent_files"), idFileOpenRecentFileClearHistory, wxID_CBFILE01),
          m_projectsHistory(wxT("文件"), wxT("/recent_projects"), idFileOpenRecentProjectClearHistory, wxID_CBFILE17),
          m_pCloseFullScreenBtn(nullptr),
          m_pEdMan(nullptr),
          m_pPrjMan(nullptr),
          m_pPrjManUI(nullptr),
          m_pLogMan(nullptr),
          m_pInfoPane(nullptr),
          m_pToolbar(nullptr),
          m_ToolsMenu(nullptr),
          m_HelpPluginsMenu(nullptr),
          m_ScanningForPlugins(false),
          m_StartupDone(false), // one-time flag
          m_InitiatedShutdown(false),
          m_AutoHideLockCounter(0),
          m_LastCtrlAltTabWindow(0),
          m_LastLayoutIsTemp(false),
          m_pScriptConsole(nullptr),
          m_pBatchBuildDialog(nullptr),
          m_pHighlightButton(nullptr) {

    Manager::Get()->GetLogManager()->DebugLog(wxString::FromUTF8("--13.1.1向管理者提供主框架[MainFrame]的句柄(this)..."));
    /*向管理器提供主框架的句柄(this)*/
    Manager::Get(this);
    Manager::Get()->GetLogManager()->DebugLog(wxString::FromUTF8("--13.1.2注册事件接收器..."));
    /*注册事件接收器*/
    RegisterEvents();

    /*允许将文件拖放到编辑器中*/
    Manager::Get()->GetLogManager()->DebugLog(wxString::FromUTF8("--13.1.3设置允许在当前框架拖放文件进入到编辑器..."));
    SetDropTarget(new cbFileDropTarget(this));
    Manager::Get()->GetLogManager()->DebugLog(wxString::FromUTF8("--13.1.4定义和设置快捷键表..."));
    /*快捷键表*/
    wxAcceleratorEntry entries[8];
    entries[0].Set(wxACCEL_CTRL | wxACCEL_SHIFT, (int) 'W', idFileCloseAll);
    entries[1].Set(wxACCEL_CTRL | wxACCEL_SHIFT, WXK_F4, idFileCloseAll);
    entries[2].Set(wxACCEL_CTRL, (int) 'W', idFileClose);
    entries[3].Set(wxACCEL_CTRL, WXK_F4, idFileClose);
    entries[4].Set(wxACCEL_CTRL, WXK_F6, idFileNext);
    entries[5].Set(wxACCEL_CTRL | wxACCEL_SHIFT, WXK_F6, idFilePrev);
    entries[6].Set(wxACCEL_SHIFT, WXK_TAB, idShiftTab);
    entries[7].Set(wxACCEL_CTRL | wxACCEL_ALT, WXK_TAB, idCtrlAltTab);
    m_pAccel = new wxAcceleratorTable(8, entries);

    SetAcceleratorTable(*m_pAccel);


    /*为支持的项目/工作区添加文件筛选器*/
    Manager::Get()->GetLogManager()->DebugLog(wxString::FromUTF8("--13.1.5增加缺省支持项目和工作空间的文件筛选器..."));
    FileFilters::AddDefaultFileFilters();
    Manager::Get()->GetLogManager()->DebugLog(wxString::FromUTF8("--13.1.6读取应用配置并读取其中工具栏布局状态..."));
    ConfigManager *cfg = Manager::Get()->GetConfigManager(_T("app"));
    m_SmallToolBar = cfg->ReadBool(_T("/environment/toolbar_size"), true);

    Manager::Get()->GetLogManager()->DebugLog(wxString::FromUTF8("--13.1.7创建IDE..."));
    CreateIDE();

#ifdef __WXMSW__
    SetIcon(wxICON(A_MAIN_ICON));
#else
    Manager::Get()->GetLogManager()->DebugLog(wxString::FromUTF8("--13.1.8设置图标..."));
    SetIcon(wxIcon(app));
#endif // __WXMSW__


    /*即使此时状态栏不可见,也可以创建状态栏,以便插件可以在其上创建自己的字段:*/
    Manager::Get()->GetLogManager()->DebugLog(wxString::FromUTF8("--13.1.9创建状态条..."));
    DoCreateStatusBar();
    SetStatusText(wxString::Format(wxT("欢迎使用%s!") , appglobals::AppName.wx_str()));

    wxStatusBar *sb = GetStatusBar();
    if (sb) {
        sb->Show(cfg->ReadBool(_T("/main_frame/statusbar"), true));
    }

    SetTitle(appglobals::AppName + _T(" v") + appglobals::AppVersion);

    Manager::Get()->GetLogManager()->DebugLog(wxString::FromUTF8("--13.1.10调整主框架大小..."));
    LoadWindowSize();

    Manager::Get()->GetLogManager()->DebugLog(wxString::FromUTF8("--13.1.11扫描插件..."));
    ScanForPlugins();

    Manager::Get()->GetLogManager()->DebugLog(wxString::FromUTF8("--13.1.11.0扫描插件完成..."));

    Manager::Get()->GetLogManager()->DebugLog(wxString::FromUTF8("--13.1.12创建工具条..."));
    CreateToolbars();

    /*代码自动补全插件管理器*/
    Manager::Get()->GetCCManager();

    Manager::Get()->GetLogManager()->DebugLog(wxString::FromUTF8("--13.1.13设置主框架布局..."));
    /*保存默认视图*/
    wxString deflayout = cfg->Read(_T("/main_frame/layout/default"));
    if (deflayout.IsEmpty()) {
        cfg->Write(_T("/main_frame/layout/default"), gDefaultLayout);
    }

    /*调整工具栏布局*/
    DoFixToolbarsLayout();

    /*保存透视图作为缺省视图*/
    gDefaultLayoutData = m_LayoutManager.SavePerspective();
    gDefaultMessagePaneLayoutData = m_pInfoPane->SaveTabOrder();
    SaveViewLayout(gDefaultLayout, gDefaultLayoutData, gDefaultMessagePaneLayoutData);
    /*生成默认最小布局*/
    wxAuiPaneInfoArray &panes = m_LayoutManager.GetAllPanes();
    for (size_t i = 0; i < panes.GetCount(); ++i) {
        wxAuiPaneInfo &info = panes[i];
        /*不是主面板隐藏信息*/
        if (!(info.name == _T("MainPane"))) { info.Hide(); }
    }
    gMinimalLayoutData = m_LayoutManager.SavePerspective();
    gMinimalMessagePaneLayoutData = m_pInfoPane->SaveTabOrder();
    SaveViewLayout(gMinimalLayout, gMinimalLayoutData, gMinimalMessagePaneLayoutData);
    Manager::Get()->GetLogManager()->DebugLog(wxString::FromUTF8("--13.1.14设置窗口状态..."));
    LoadWindowState();
    Manager::Get()->GetLogManager()->DebugLog(wxString::FromUTF8("--13.1.15显示起始页..."));
    ShowHideStartPage();
    Manager::Get()->GetLogManager()->DebugLog(wxString::FromUTF8("--13.1.16注册脚本函数并运行..."));
    RegisterScriptFunctions();
    RunStartupScripts();
    Manager::Get()->GetLogManager()->DebugLog(_T("--13.1.17正在初始化插件..."));
}

MainFrame::~MainFrame() {
    SetAcceleratorTable(wxNullAcceleratorTable);
    delete m_pAccel;
    DeInitPrinting();
    delete m_debuggerMenuHandler;
    delete m_debuggerToolbarHandler;
}

/*注册事件*/
void MainFrame::RegisterEvents() {
    Manager *m = Manager::Get();

    m->RegisterEventSink(cbEVT_EDITOR_UPDATE_UI, new cbEventFunctor<MainFrame, CodeBlocksEvent>(this, &MainFrame::OnEditorUpdateUI));

    m->RegisterEventSink(cbEVT_PROJECT_ACTIVATE, new cbEventFunctor<MainFrame, CodeBlocksEvent>(this, &MainFrame::OnProjectActivated));
    m->RegisterEventSink(cbEVT_PROJECT_OPEN, new cbEventFunctor<MainFrame, CodeBlocksEvent>(this, &MainFrame::OnProjectOpened));
    m->RegisterEventSink(cbEVT_PROJECT_CLOSE, new cbEventFunctor<MainFrame, CodeBlocksEvent>(this, &MainFrame::OnProjectClosed));
    m->RegisterEventSink(cbEVT_EDITOR_CLOSE, new cbEventFunctor<MainFrame, CodeBlocksEvent>(this, &MainFrame::OnEditorClosed));
    m->RegisterEventSink(cbEVT_EDITOR_OPEN, new cbEventFunctor<MainFrame, CodeBlocksEvent>(this, &MainFrame::OnEditorOpened));
    m->RegisterEventSink(cbEVT_EDITOR_ACTIVATED, new cbEventFunctor<MainFrame, CodeBlocksEvent>(this, &MainFrame::OnEditorActivated));
    m->RegisterEventSink(cbEVT_EDITOR_SAVE, new cbEventFunctor<MainFrame, CodeBlocksEvent>(this, &MainFrame::OnEditorSaved));
    m->RegisterEventSink(cbEVT_EDITOR_MODIFIED, new cbEventFunctor<MainFrame, CodeBlocksEvent>(this, &MainFrame::OnEditorModified));

    m->RegisterEventSink(cbEVT_ADD_DOCK_WINDOW, new cbEventFunctor<MainFrame, CodeBlocksDockEvent>(this, &MainFrame::OnRequestDockWindow));
    m->RegisterEventSink(cbEVT_REMOVE_DOCK_WINDOW, new cbEventFunctor<MainFrame, CodeBlocksDockEvent>(this, &MainFrame::OnRequestUndockWindow));
    m->RegisterEventSink(cbEVT_SHOW_DOCK_WINDOW, new cbEventFunctor<MainFrame, CodeBlocksDockEvent>(this, &MainFrame::OnRequestShowDockWindow));
    m->RegisterEventSink(cbEVT_HIDE_DOCK_WINDOW, new cbEventFunctor<MainFrame, CodeBlocksDockEvent>(this, &MainFrame::OnRequestHideDockWindow));
    m->RegisterEventSink(cbEVT_DOCK_WINDOW_VISIBILITY, new cbEventFunctor<MainFrame, CodeBlocksDockEvent>(this, &MainFrame::OnDockWindowVisibility));

    m->RegisterEventSink(cbEVT_PLUGIN_ATTACHED, new cbEventFunctor<MainFrame, CodeBlocksEvent>(this, &MainFrame::OnPluginLoaded));
    m->RegisterEventSink(cbEVT_PLUGIN_RELEASED, new cbEventFunctor<MainFrame, CodeBlocksEvent>(this, &MainFrame::OnPluginUnloaded));
    m->RegisterEventSink(cbEVT_PLUGIN_INSTALLED, new cbEventFunctor<MainFrame, CodeBlocksEvent>(this, &MainFrame::OnPluginInstalled));
    m->RegisterEventSink(cbEVT_PLUGIN_UNINSTALLED, new cbEventFunctor<MainFrame, CodeBlocksEvent>(this, &MainFrame::OnPluginUninstalled));

    m->RegisterEventSink(cbEVT_UPDATE_VIEW_LAYOUT, new cbEventFunctor<MainFrame, CodeBlocksLayoutEvent>(this, &MainFrame::OnLayoutUpdate));
    m->RegisterEventSink(cbEVT_QUERY_VIEW_LAYOUT, new cbEventFunctor<MainFrame, CodeBlocksLayoutEvent>(this, &MainFrame::OnLayoutQuery));
    m->RegisterEventSink(cbEVT_SWITCH_VIEW_LAYOUT, new cbEventFunctor<MainFrame, CodeBlocksLayoutEvent>(this, &MainFrame::OnLayoutSwitch));

    m->RegisterEventSink(cbEVT_ADD_LOG_WINDOW, new cbEventFunctor<MainFrame, CodeBlocksLogEvent>(this, &MainFrame::OnAddLogWindow));
    m->RegisterEventSink(cbEVT_REMOVE_LOG_WINDOW, new cbEventFunctor<MainFrame, CodeBlocksLogEvent>(this, &MainFrame::OnRemoveLogWindow));
    m->RegisterEventSink(cbEVT_HIDE_LOG_WINDOW, new cbEventFunctor<MainFrame, CodeBlocksLogEvent>(this, &MainFrame::OnHideLogWindow));
    m->RegisterEventSink(cbEVT_SWITCH_TO_LOG_WINDOW, new cbEventFunctor<MainFrame, CodeBlocksLogEvent>(this, &MainFrame::OnSwitchToLogWindow));
    m->RegisterEventSink(cbEVT_GET_ACTIVE_LOG_WINDOW, new cbEventFunctor<MainFrame, CodeBlocksLogEvent>(this, &MainFrame::OnGetActiveLogWindow));
    m->RegisterEventSink(cbEVT_SHOW_LOG_MANAGER, new cbEventFunctor<MainFrame, CodeBlocksLogEvent>(this, &MainFrame::OnShowLogManager));
    m->RegisterEventSink(cbEVT_HIDE_LOG_MANAGER, new cbEventFunctor<MainFrame, CodeBlocksLogEvent>(this, &MainFrame::OnHideLogManager));
    m->RegisterEventSink(cbEVT_LOCK_LOG_MANAGER, new cbEventFunctor<MainFrame, CodeBlocksLogEvent>(this, &MainFrame::OnLockLogManager));
    m->RegisterEventSink(cbEVT_UNLOCK_LOG_MANAGER, new cbEventFunctor<MainFrame, CodeBlocksLogEvent>(this, &MainFrame::OnUnlockLogManager));
}

/*显示操作提示*/
void MainFrame::ShowTips(bool forceShow) {
    bool showAtStartup = Manager::Get()->GetConfigManager(_T("app"))->ReadBool(_T("/show_tips"), false);
    if (forceShow || showAtStartup) {
        wxString tipsFile = ConfigManager::GetDataFolder() + _T("/tips.txt");
        long tipsIndex = Manager::Get()->GetConfigManager(_T("app"))->ReadInt(_T("/next_tip"), 0);
        wxTipProvider *tipProvider = wxCreateFileTipProvider(tipsFile, tipsIndex);
        showAtStartup = wxShowTip(this, tipProvider, showAtStartup);
        Manager::Get()->GetConfigManager(_T("app"))->Write(_T("/show_tips"), showAtStartup);
        Manager::Get()->GetConfigManager(_T("app"))->Write(_T("/next_tip"), (int) tipProvider->GetCurrentTip());
        delete tipProvider;
    }
}

/*在主框架创建开发面板布局*/
void MainFrame::CreateIDE() {
    Manager::Get()->GetLogManager()->DebugLog(wxString::FromUTF8("--13.1.7.1设置左窗口宽度默认[200]..."));
    int leftW = Manager::Get()->GetConfigManager(_T("app"))->ReadInt(_T("/main_frame/layout/left_block_width"), 200);
    wxSize clientsize = GetClientSize();
    Manager::Get()->GetLogManager()->DebugLog(wxString::FromUTF8("--13.1.7.2创建关闭全屏按钮并在初始化时隐藏它..."));
    /*创建关闭全屏按钮并在初始化时隐藏它*/
    m_pCloseFullScreenBtn = new wxButton(this, idCloseFullScreen, wxT("关闭全屏"), wxDefaultPosition);
    m_pCloseFullScreenBtn->Show(false);
    Manager::Get()->GetLogManager()->DebugLog(wxString::FromUTF8("--13.1.7.3创建项目管理器..."));
    /*面板管理*/
    m_pPrjMan = Manager::Get()->GetProjectManager();
    if (!Manager::IsBatchBuild()) {
        m_pPrjManUI = new ProjectManagerUI;
        m_LayoutManager.AddPane(m_pPrjManUI->GetNotebook(),
                                wxAuiPaneInfo().Name(wxT("ManagementPane")).Caption(wxT("管理器面板")).
                                        BestSize(wxSize(leftW, clientsize.GetHeight())).
                                        MinSize(wxSize(100, 100)).Left().Layer(1));
    } else {
        m_pPrjManUI = new BatchProjectManagerUI;
    }
    m_pPrjMan->SetUI(m_pPrjManUI);
    /*日志管理器*/
    Manager::Get()->GetLogManager()->DebugLog(wxString::FromUTF8("--13.1.7.4设置图形日志..."));
    SetupGUILogging();
    Manager::Get()->GetLogManager()->DebugLog(wxString::FromUTF8("--13.1.7.5设置调试界面..."));
    SetupDebuggerUI();
    Manager::Get()->GetLogManager()->DebugLog(wxString::FromUTF8("--13.1.7.6创建和载入菜单栏..."));
    CreateMenubar();
    m_pEdMan = Manager::Get()->GetEditorManager();
    m_pLogMan = Manager::Get()->GetLogManager();
    /*编辑器管理器*/
    m_LayoutManager.AddPane(m_pEdMan->GetNotebook(), wxAuiPaneInfo().Name(wxT("MainPane")).CentrePane());
    /*控制台脚本*/
    m_pScriptConsole = new ScriptConsole(this, -1);
    m_LayoutManager.AddPane(m_pScriptConsole, wxAuiPaneInfo().Name(wxT("ScriptConsole")).
            Caption(wxT("脚本控制台")).Float().MinSize(100, 100).FloatingPosition(300, 200).Hide());
    Manager::Get()->GetLogManager()->DebugLog(wxString::FromUTF8("--13.1.7.7刷新主框架布局..."));
    DoUpdateLayout();
    DoUpdateLayoutColours();
    DoUpdateEditorStyle();
    m_pEdMan->GetNotebook()->SetDropTarget(new cbFileDropTarget(this));
    if (m_pPrjManUI->GetNotebook()) {
        m_pPrjManUI->GetNotebook()->SetDropTarget(new cbFileDropTarget(this));
    }
    Manager::Get()->GetColourManager()->Load();
}

/*设置图形界面日志框架*/
void MainFrame::SetupGUILogging() {

    /*允许新的停靠窗口使用可用空间的3/4,默认值(0.3)有时太小,特别是对于"日志和其他"标签*/
    m_LayoutManager.SetDockSizeConstraint(0.75, 0.75);
    int bottomH = Manager::Get()->GetConfigManager(_T("app"))->ReadInt(_T("/main_frame/layout/bottom_block_height"), 150);
    wxSize clientsize = GetClientSize();
    LogManager *mgr = Manager::Get()->GetLogManager();
    if (!Manager::IsBatchBuild()) {
        m_pInfoPane = new InfoPane(this);
        m_LayoutManager.AddPane(m_pInfoPane, wxAuiPaneInfo().
                Name(wxT("MessagesPane")).Caption(wxT("日志和其他")).
                BestSize(wxSize(clientsize.GetWidth(), bottomH)).
                Bottom());
        /*应用日志*/
        wxWindow *log;
        for (size_t i = LogManager::app_log; i < LogManager::max_logs; ++i) {
            if ((log = mgr->Slot(i).GetLogger()->CreateControl(m_pInfoPane)))
                m_pInfoPane->AddLogger(mgr->Slot(i).GetLogger(), log, mgr->Slot(i).title, mgr->Slot(i).icon);
        }
        m_findReplace.CreateSearchLog();
    } else {
        m_pBatchBuildDialog = new BatchLogWindow(this, wxT("Code::Blocks批构建"));
        wxSizer *s = new wxBoxSizer(wxVERTICAL);
        m_pInfoPane = new InfoPane(m_pBatchBuildDialog);
        s->Add(m_pInfoPane, 1, wxEXPAND);
        m_pBatchBuildDialog->SetSizer(s);
        /*由于某些原因,设置&g_null_log会导致应用程序在退出时崩溃...*/
        mgr->SetLog(new NullLogger, LogManager::app_log);
        mgr->SetLog(new NullLogger, LogManager::debug_log);
    }

    mgr->NotifyUpdate();
    m_pInfoPane->SetDropTarget(new cbFileDropTarget(this));
}

/*设置调试界面*/
void MainFrame::SetupDebuggerUI() {
    m_debuggerMenuHandler = new DebuggerMenuHandler;
    m_debuggerToolbarHandler = new DebuggerToolbarHandler(m_debuggerMenuHandler);
    m_debuggerMenuHandler->SetEvtHandlerEnabled(false);
    m_debuggerToolbarHandler->SetEvtHandlerEnabled(false);
    wxWindow *window = Manager::Get()->GetAppWindow();
    if (window) {
        window->PushEventHandler(m_debuggerMenuHandler);
        window->PushEventHandler(m_debuggerToolbarHandler);
    }
    m_debuggerMenuHandler->SetEvtHandlerEnabled(true);
    m_debuggerToolbarHandler->SetEvtHandlerEnabled(true);

    if (!Manager::IsBatchBuild()) {
        Manager::Get()->GetDebuggerManager()->SetInterfaceFactory(new DebugInterfaceFactory);
        m_debuggerMenuHandler->RegisterDefaultWindowItems();
    }
}

/*声明实例类型*/
DECLARE_INSTANCE_TYPE(MainFrame);
/*注册脚本函数*/
void MainFrame::RegisterScriptFunctions() {
    SqPlus::SQClassDef<MainFrame>("MainFrame").func(&MainFrame::Open, "Open");
    SqPlus::BindVariable(this, "App", SqPlus::VAR_ACCESS_READ_ONLY);
}

/*运行启动脚本*/
void MainFrame::RunStartupScripts() {
    ConfigManager *mgr = Manager::Get()->GetConfigManager(_T("scripting"));
    wxArrayString keys = mgr->EnumerateKeys(_T("/startup_scripts"));

    for (size_t i = 0; i < keys.GetCount(); ++i) {
        ScriptEntry se;
        wxString ser;
        if (mgr->Read(_T("/startup_scripts/") + keys[i], &ser)) {
            se.SerializeIn(ser);
            if (!se.enabled) {
                continue;
            }

            try {
                wxString startup = se.script;
                if (wxFileName(se.script).IsRelative())
                    startup = ConfigManager::LocateDataFile(se.script, sdScriptsUser | sdScriptsGlobal);
                if (!startup.IsEmpty()) {
                    if (!se.registered) {
                        Manager::Get()->GetScriptingManager()->LoadScript(startup);
                    }else if (!se.menu.IsEmpty()) {
                        Manager::Get()->GetScriptingManager()->RegisterScriptMenu(se.menu, startup, false);
                    }else {
                        Manager::Get()->GetLogManager()->LogWarning(wxString::Format(wxT("启动脚本/函数 '%s' 未被载入: 无效配置"), se.script.wx_str()));
                    }
                } else {
                    Manager::Get()->GetLogManager()->LogWarning(wxString::Format(wxT("未找到启动脚本'%s'"), se.script.wx_str()));
                }
            }
            catch (SquirrelError &exception) {
                Manager::Get()->GetScriptingManager()->DisplayErrors(&exception);
            }
        }
    }
}

/*插件被更新*/
void MainFrame::PluginsUpdated(cb_unused cbPlugin *plugin, cb_unused int status) {
    Freeze();
    /*重建菜单栏*/
    RecreateMenuBar();
    /*更新"视图->工具条",因为我们重建了菜单栏*/
    PluginElementsArray plugins = Manager::Get()->GetPluginManager()->GetPlugins();
    for (unsigned int i = 0; i < plugins.GetCount(); ++i) {
        cbPlugin *plug = plugins[i]->plugin;
        const PluginInfo *info = Manager::Get()->GetPluginManager()->GetPluginInfo(plug);
        if (!info) {
            continue;
        }
        /*如果插件存在工具栏*/
        if (m_PluginsTools[plug])
        {
            /*工具栏存在添加菜单项*/
            wxMenu *viewToolbars = nullptr;
            GetMenuBar()->FindItem(idViewToolMain, &viewToolbars);
            if (viewToolbars) {
                if (viewToolbars->FindItem(info->title) != wxNOT_FOUND) {
                    continue;
                }
                wxMenuItem *item = AddPluginInMenus(viewToolbars, plug,
                                                    (wxObjectEventFunction) (wxEventFunction) (wxCommandEventFunction) &MainFrame::OnToggleBar,
                                                    -1, true);
                if (item) {
                    item->Check(IsWindowReallyShown(m_PluginsTools[plug]));
                }
            }
        }
    }

    Thaw();
}

/*重建菜单栏*/
void MainFrame::RecreateMenuBar() {
    Freeze();
    wxMenuBar *m = GetMenuBar();
    /*解锁旧菜单*/
    SetMenuBar(nullptr);
    /*创建新菜单*/
    CreateMenubar();
    /*删除旧菜单*/
    delete m;
    /*更新布局菜单*/
    for (LayoutViewsMap::iterator it = m_LayoutViews.begin(); it != m_LayoutViews.end(); ++it) {
        if (it->first.IsEmpty()) {continue;}
        SaveViewLayout(it->first, it->second,m_LayoutMessagePane[it->first],it->first == m_LastLayoutName);
    }
    /*冷冻后重新绘制*/
    Thaw();
}

/*创建菜单栏*/
void MainFrame::CreateMenubar() {
    Manager::Get()->GetLogManager()->DebugLog(wxString::FromUTF8("--13.1.7.6.1定义和链接菜单处理事件..."));
    CodeBlocksEvent event(cbEVT_MENUBAR_CREATE_BEGIN);
    Manager::Get()->ProcessEvent(event);
    Manager::Get()->GetLogManager()->DebugLog(wxString::FromUTF8("--13.1.7.6.2定义菜单及菜单项并载入菜单资源..."));
    int tmpidx;
    wxMenuBar *mbar = nullptr;
    wxMenu *hl = nullptr, *tools = nullptr, *plugs = nullptr, *pluginsM = nullptr;
    wxMenuItem *tmpitem = nullptr;
    wxXmlResource *xml_res = wxXmlResource::Get();
    wxString resPath = ConfigManager::GetDataFolder();
    Manager::Get()->GetLogManager()->DebugLog(wxString::FromUTF8(_("--13.1.7.6.2a菜单资源文件路径[" + resPath + _T("/resources.zip#zip:main_menu.xrc") + "]...")));
    xml_res->Load(resPath + _T("/resources.zip#zip:main_menu.xrc"));

    Manager::Get()->GetLogManager()->DebugLog(wxString::FromUTF8("--13.1.7.6.2b载入菜单栏并设置..."));
    mbar = xml_res->LoadMenuBar(_T("main_menu_bar"));
    if (!mbar){mbar = new wxMenuBar();}
    if (mbar){SetMenuBar(mbar);}
    Manager::Get()->GetLogManager()->DebugLog(wxString::FromUTF8("--13.1.7.6.2c5.0菜单列表..."));
    wxMenu *m;
    wxString m_tt;
    for (int i = 0; i < (int) mbar->GetMenuCount(); i++) {
        m = mbar->GetMenu(i);
        m_tt = m->GetTitle();
        Manager::Get()->GetLogManager()->DebugLog(wxString::Format(_T("--13.1.7.6.2c5.0- %i 菜单[t:%s]"), i, m_tt.wx_str()));
    }
    Manager::Get()->GetLogManager()->DebugLog(wxString::FromUTF8("--13.1.7.6.2c找到我们稍后更改的菜单..."));
    tmpidx = mbar->FindMenu(wxT("编辑"));
    if (tmpidx != wxNOT_FOUND) {
        mbar->FindItem(idEditHighlightModeText, &hl);
        if (hl) {
            EditorColourSet *colour_set = Manager::Get()->GetEditorManager()->GetColourSet();
            if (colour_set) {
                wxArrayString langs = colour_set->GetAllHighlightLanguages();
                for (size_t i = 0; i < langs.GetCount(); ++i) {
                    /*每20个项目分成列*/
                    if (i > 0 && !(i % 20)){hl->Break();}
                    int id = wxNewId();
                    hl->AppendRadioItem(id, langs[i],wxString::Format(wxT("将当前文档的突出显示模式切换到 \"%s\""), langs[i].wx_str()));
                    /*
                     * 连接和断开
                     * 这些函数用于旧的、非类型化的事件处理程序,不检查传递给它们的函数类型是否与事件类型实际匹配.
                     * 它们还只允许将事件连接到wxEvtHandler派生类的方法.
                     * 下面的template Connect()方法更安全,允许将事件连接到任意函数或函数,但需要编译器对模板的支持.
                     * 成员函数处理程序与事件处理程序,winid和事件类型的动态关联
                    */
                    Connect(id, -1, wxEVT_COMMAND_MENU_SELECTED,
                            (wxObjectEventFunction) (wxEventFunction) (wxCommandEventFunction)
                                    &MainFrame::OnEditHighlightMode);
                }
            }
        }
        const wxLanguageInfo *info = wxLocale::GetLanguageInfo(wxLANGUAGE_DEFAULT);
        wxMenu *editMenu = mbar->GetMenu(tmpidx);
        /*规避输入法快捷键*/
        if (info
            && ((info->Language >= wxLANGUAGE_CHINESE
                 && info->Language <= wxLANGUAGE_CHINESE_TAIWAN)
                || info->Language == wxLANGUAGE_JAPANESE
                || info->Language == wxLANGUAGE_KOREAN)) {
            editMenu->Append(idEditCompleteCode, wxT("完成代码按\tShift-Space"));
        } else {
            editMenu->Append(idEditCompleteCode, wxT("完成代码按\tCtrl-Space"));
        }
    }

    tmpidx = mbar->FindMenu(wxT("工具"));
    Manager::Get()->GetLogManager()->DebugLog(wxString::FromUTF8("--13.1.7.6.2c2工具" + wxString::Format("%i", tmpidx) + "菜单..."));
    if (tmpidx != wxNOT_FOUND) {
        tools = mbar->GetMenu(tmpidx);
    }

    tmpidx = mbar->FindMenu(wxT("插件"));
    Manager::Get()->GetLogManager()->DebugLog(wxString::FromUTF8("--13.1.7.6.2c3插件" + wxString::Format("%i", tmpidx) + "菜单..."));
    if (tmpidx != wxNOT_FOUND) {
        plugs = mbar->GetMenu(tmpidx);
    }
    Manager::Get()->GetLogManager()->DebugLog(wxString::FromUTF8("--13.1.7.6.2c4关于插件菜单项..."));
    if ((tmpitem = mbar->FindItem(idHelpPlugins, nullptr))) {
        pluginsM = tmpitem->GetSubMenu();
    }

    m_ToolsMenu = tools ? tools : new wxMenu();
    m_PluginsMenu = plugs ? plugs : new wxMenu();
    m_HelpPluginsMenu = pluginsM ? pluginsM : new wxMenu();

    /*核心模块:创建菜单*/
    Manager::Get()->GetLogManager()->DebugLog(wxString::FromUTF8("--13.1.7.6.2c5创建菜单..."));

    if (!Manager::IsBatchBuild()) static_cast<ProjectManagerUI *>(m_pPrjManUI)->CreateMenu(mbar);

    Manager::Get()->GetLogManager()->DebugLog(wxString::FromUTF8("--13.1.7.6.2c6设置菜单调试器..."));
    Manager::Get()->GetDebuggerManager()->SetMenuHandler(m_debuggerMenuHandler);

    /*要求所有插件重新构建它们的菜单*/
    PluginElementsArray plugins = Manager::Get()->GetPluginManager()->GetPlugins();
    int pc = plugins.GetCount();
    Manager::Get()->GetLogManager()->DebugLog(wxString::FromUTF8("--13.1.7.6.2c7获取插件数" + wxString::Format(wxT("%i"), pc)));
    for (unsigned int i = 0; i < plugins.GetCount(); ++i) {
        cbPlugin *plug = plugins[i]->plugin;
        if (plug && plug->IsAttached()) {
            if (plug->GetType() == ptTool) {
                DoAddPlugin(plug);
            }else {
                AddPluginInHelpPluginsMenu(plug);
                try {
                    plug->BuildMenu(mbar);
                }
                catch (cbException &e) {
                    e.ShowErrorMessage();
                }
            }
        }
    }

    Manager::Get()->GetLogManager()->DebugLog(wxString::FromUTF8("--13.1.7.6.2c8构建菜单"));
    Manager::Get()->GetToolsManager()->BuildToolsMenu(m_ToolsMenu);
    Manager::Get()->GetLogManager()->DebugLog(wxString::FromUTF8("--13.1.7.6.2c8构建菜单完成----"));
    /*Ctrl+Tab非windows平台的解决方法:*/
    if ((platform::carbon) || (platform::gtk)) {
        /*找到用于切换选项卡的菜单项:*/
        Manager::Get()->GetLogManager()->DebugLog(wxString::FromUTF8("--13.1.7.6.2c9视图菜单..."));
        tmpidx = mbar->FindMenu(wxT("视图"));
        if (tmpidx != wxNOT_FOUND) {
            wxMenu *view = mbar->GetMenu(tmpidx);
            wxMenuItem *switch_item = view->FindItem(idViewSwitchTabs);
            if (switch_item) {
                /*更改此菜单项的快捷键:*/
                wxString accel;
                if (platform::carbon) {
                    accel = wxT("Alt+Tab");
                }else if (platform::gtk) {
                    accel = wxT("Ctrl+,");
                }
                switch_item->SetItemLabel(wxString::Format(wxT("切换标签\t%s"),accel.wx_str()));
            }
        }
    }

    Manager::Get()->GetLogManager()->DebugLog(wxString::FromUTF8("--13.1.7.6.3置入菜单栏..."));

    SetMenuBar(mbar);

    Manager::Get()->GetLogManager()->DebugLog(wxString::FromUTF8("--13.1.7.6.4初始化最近打开的文件历史..."));
    InitializeRecentFilesHistory();

    Manager::Get()->GetLogManager()->DebugLog(wxString::FromUTF8("--13.1.7.6.5连结菜单处理事件..."));

    CodeBlocksEvent event2(cbEVT_MENUBAR_CREATE_END);
    Manager::Get()->ProcessEvent(event2);
}

/*创建工具栏*/
void MainFrame::CreateToolbars() {
    if (m_pToolbar) {
        SetToolBar(nullptr);
        m_pToolbar = nullptr;
    }

    wxString xrcToolbarName(_T("main_toolbar"));
    /*在此处插入逻辑?*/
    if (m_SmallToolBar) {
        xrcToolbarName += _T("_16x16");
    }

    wxXmlResource *xml_res = wxXmlResource::Get();
    wxString resPath = ConfigManager::GetDataFolder();
    xml_res->Load(resPath + _T("/resources.zip#zip:") + xrcToolbarName + _T("*.xrc"));
    m_pToolbar = Manager::Get()->CreateEmptyToolbar();
    Manager::Get()->AddonToolBar(m_pToolbar, xrcToolbarName);
    m_pToolbar->Realize();
    /*在主工具栏右键点击弹出上下文菜单*/
    m_pToolbar->Connect(wxID_ANY, wxEVT_COMMAND_TOOL_RCLICKED, wxCommandEventHandler(MainFrame::OnToolBarRightClick), NULL, this);
    m_pToolbar->SetInitialSize();
    /*在调试工具栏右键点击弹出上下文菜单*/

    m_debuggerToolbarHandler->GetToolbar()->Connect(wxID_ANY, wxEVT_COMMAND_TOOL_RCLICKED, wxCommandEventHandler(MainFrame::OnToolBarRightClick), NULL, this);
    std::vector<ToolbarInfo> toolbars;
    toolbars.push_back(ToolbarInfo(m_pToolbar, wxAuiPaneInfo().Name(wxT("MainToolbar")).Caption(wxT("主工具栏")), 0));
    toolbars.push_back(ToolbarInfo(m_debuggerToolbarHandler->GetToolbar(),
                                   wxAuiPaneInfo().Name(wxT("DebuggerToolbar")).Caption(wxT("调试器工具栏")),
                                   2));

    /*要求所有插件重新构建它们的工具栏*/
    PluginElementsArray plugins = Manager::Get()->GetPluginManager()->GetPlugins();
    for (unsigned int i = 0; i < plugins.GetCount(); ++i) {
        cbPlugin *plug = plugins[i]->plugin;
        if (plug && plug->IsAttached()) {
            ToolbarInfo info = DoAddPluginToolbar(plug);
            if (info.toolbar) {
                toolbars.push_back(info);
                /*支持显示插件工具栏的上下文菜单*/
                info.toolbar->Connect(wxID_ANY, wxEVT_COMMAND_TOOL_RCLICKED,
                                      wxCommandEventHandler(MainFrame::OnToolBarRightClick), NULL, this);
            }
        }
    }
    std::sort(toolbars.begin(), toolbars.end());
    int row = 0, position = 0, rowLength = 0;
    int maxLength = GetSize().x;

    for (std::vector<ToolbarInfo>::iterator it = toolbars.begin(); it != toolbars.end(); ++it) {
        rowLength += it->toolbar->GetSize().x;
        if (rowLength >= maxLength) {
            row++;
            position = 0;
            rowLength = it->toolbar->GetSize().x;
        }
        wxAuiPaneInfo paneInfo(it->paneInfo);
        m_LayoutManager.AddPane(it->toolbar, paneInfo.ToolbarPane().Top().Row(row).Position(position));
        position += it->toolbar->GetSize().x;
    }
    DoUpdateLayout();
    Manager::ProcessPendingEvents();
    SetToolBar(nullptr);
}

void MainFrame::AddToolbarItem(int id, const wxString &title, const wxString &shortHelp, const wxString &longHelp, const wxString &image) {
    m_pToolbar->AddTool(id, title, cbLoadBitmap(image, wxBITMAP_TYPE_PNG));
    m_pToolbar->SetToolShortHelp(id, shortHelp);
    m_pToolbar->SetToolLongHelp(id, longHelp);
}

void MainFrame::ScanForPlugins() {
    m_ScanningForPlugins = true;
    m_PluginIDsMap.clear();

    PluginManager *m_PluginManager = Manager::Get()->GetPluginManager();

    /*用户路径优先*/
    wxString path = ConfigManager::GetPluginsFolder(false);
    Manager::Get()->GetLogManager()->DebugLog(wxString::Format(wxT("正在扫描插件,在用户目录: %s"),path.wx_str()));
    int count = m_PluginManager->ScanForPlugins(path);

    /*全局路径*/
    path = ConfigManager::GetPluginsFolder(true);
    Manager::Get()->GetLogManager()->DebugLog(wxString::Format(wxT("正在扫描插件,在全局目录: %s"), path.wx_str()));
    count += m_PluginManager->ScanForPlugins(path);

    /*实际加载插件*/
    if (count > 0) {
        Manager::Get()->GetLogManager()->DebugLog(wxString::FromUTF8("开始载入所有插件..."));
        m_PluginManager->LoadAllPlugins();
    }

    CodeBlocksEvent event(cbEVT_PLUGIN_LOADING_COMPLETE);
    Manager::Get()->GetLogManager()->DebugLog(wxString::FromUTF8("触发通知事件1..."));
    Manager::Get()->GetPluginManager()->NotifyPlugins(event);
    Manager::Get()->GetLogManager()->DebugLog(wxString::FromUTF8("触发通知事件2..."));
    m_ScanningForPlugins = false;
}

wxMenuItem *MainFrame::AddPluginInMenus(wxMenu *menu, cbPlugin *plugin, wxObjectEventFunction callback, int pos, bool checkable) {
    wxMenuItem *item = nullptr;
    if (!plugin || !menu){return item;}
    const PluginInfo *info = Manager::Get()->GetPluginManager()->GetPluginInfo(plugin);
    if (!info){return nullptr;}

    PluginIDsMap::iterator it;
    for (it = m_PluginIDsMap.begin(); it != m_PluginIDsMap.end(); ++it) {
        if (it->second == info->name) {
            item = menu->FindItem(it->first);
            if (item){return item;}
        }
    }

    int id = wxNewId();
    wxString title = info->title + (menu == m_HelpPluginsMenu ? wxT("...") : wxEmptyString);
    m_PluginIDsMap[id] = info->name;
    if (pos == -1){pos = menu->GetMenuItemCount();}

    while (!item) {
#if wxCHECK_VERSION(3, 0, 0)
        if (!pos || title.CmpNoCase(menu->FindItemByPosition(pos - 1)->GetItemLabelText()) > 0)
#else
            if (!pos || title.CmpNoCase(menu->FindItemByPosition(pos - 1)->GetLabel()) > 0)
#endif
            item = menu->Insert(pos, id, title, wxEmptyString, checkable ? wxITEM_CHECK : wxITEM_NORMAL);

        --pos;
    }

    Connect(id, wxEVT_COMMAND_MENU_SELECTED, callback);
    return item;
}

void MainFrame::AddPluginInPluginsMenu(cbPlugin *plugin) {
    /*'插件'菜单是特殊情况下,因为它包含'管理插件',这必须留在菜单的末尾,所以我们插入条目,而不是附加...,当第一个插件被添加到“插件”菜单时,这将插入一个分隔符*/
    if (m_PluginsMenu->GetMenuItemCount() == 1){m_PluginsMenu->Insert(0, wxID_SEPARATOR, wxT(""));}

    AddPluginInMenus(m_PluginsMenu, plugin,(wxObjectEventFunction) (wxEventFunction) (wxCommandEventFunction) &MainFrame::OnPluginsExecuteMenu,m_PluginsMenu->GetMenuItemCount() - 2);
}

void MainFrame::AddPluginInHelpPluginsMenu(cbPlugin *plugin) {
    AddPluginInMenus(m_HelpPluginsMenu, plugin,
                     (wxObjectEventFunction) (wxEventFunction) (wxCommandEventFunction) &MainFrame::OnHelpPluginMenu);
}

void MainFrame::LoadWindowState() {
    wxArrayString subs = Manager::Get()->GetConfigManager(_T("app"))->EnumerateSubPaths(_T("/main_frame/layout"));
    for (size_t i = 0; i < subs.GetCount(); ++i) {
        wxString name = Manager::Get()->GetConfigManager(_T("app"))->Read(_T("/main_frame/layout/") + subs[i] + _T("/name"));
        wxString layout = Manager::Get()->GetConfigManager(_T("app"))->Read(_T("/main_frame/layout/") + subs[i] + _T("/data"));
        wxString layoutMP = Manager::Get()->GetConfigManager(_T("app"))->Read(_T("/main_frame/layout/") + subs[i] + _T("/dataMessagePane"));
        SaveViewLayout(name, layout, layoutMP);
    }
    wxString deflayout = Manager::Get()->GetConfigManager(_T("app"))->Read(_T("/main_frame/layout/default"));
    LoadViewLayout(deflayout);

    /*加载管理器和消息选定页*/
    if (m_pPrjManUI->GetNotebook())
        m_pPrjManUI->GetNotebook()->SetSelection(Manager::Get()->GetConfigManager(_T("app"))->ReadInt(_T("/main_frame/layout/left_block_selection"), 0));
    m_pInfoPane->SetSelection(Manager::Get()->GetConfigManager(_T("app"))->ReadInt(_T("/main_frame/layout/bottom_block_selection"), 0));

    /*
    由于某种原因,Cryogen 于23/3/10 wxAuiNotebook在实例化后无法设置自己的选项卡位置.此代码在InfoPane::InfoPane()中失败.
    移动到这里,因为这似乎是一个合理的地方做用户界面设置.你可以把它移到别处.
     */

    if (Manager::Get()->GetConfigManager(_T("app"))->ReadBool(_T("/environment/infopane_tabs_bottom"), false)) {
        m_pInfoPane->SetWindowStyleFlag(m_pInfoPane->GetWindowStyleFlag() | wxAUI_NB_BOTTOM);
    }
}

void MainFrame::LoadWindowSize() {
#ifndef __WXMAC__
    int x = 0;
    int y = 0;
#else
    int x = 0;
    int y = wxSystemSettings::GetMetric(wxSYS_MENU_Y, this); // make sure it doesn't hide under the menu bar
#endif
    int w = 1000;
    int h = 800;

    /*获取上次使用的显示索引*/
    int last_display_index = Manager::Get()->GetConfigManager(_T("app"))->ReadInt(_T("/main_frame/layout/display"), 0);
    /*加载窗口大小和位置*/
    wxRect rect(Manager::Get()->GetConfigManager(_T("app"))->ReadInt(_T("/main_frame/layout/left"), x),
                Manager::Get()->GetConfigManager(_T("app"))->ReadInt(_T("/main_frame/layout/top"), y),
                Manager::Get()->GetConfigManager(_T("app"))->ReadInt(_T("/main_frame/layout/width"), w),
                Manager::Get()->GetConfigManager(_T("app"))->ReadInt(_T("/main_frame/layout/height"), h));
    /*需要时最大化*/
    bool maximized = Manager::Get()->GetConfigManager(_T("app"))->ReadBool(_T("/main_frame/layout/maximized"), true);
    Maximize(maximized);

    /*设置显示,大小和位置*/
    /*C::B通常从主显示器开始...*/
    int display_index_window = wxDisplay::GetFromWindow(this);
    /*...但如果仍然可用,请尝试使用上次使用的显示:*/
    if ((last_display_index >= 0) && (last_display_index < static_cast<int>(wxDisplay::GetCount())))
        display_index_window = static_cast<int>(last_display_index);
    int display_index = ((display_index_window >= 0) ? display_index_window : 0);

    /*由于GetFromWindow调用,索引可能是wxNOT_FOUND(=-1)*/
    wxDisplay disp(display_index);
    if (maximized) {
        /*从显示应用,覆盖上面的设置*/
        rect = disp.GetClientArea();
        rect.width -= 100;
        rect.height -= 100;
    } else {
        /*调整到实际屏幕大小.这对于可移植的C::B版本很有用,当在两个监视器系统上保存一个在一个监视器系统(至少在Windows上)上重新打开的窗口时,该窗口可能不在屏幕上.*/
        wxRect displayRect = disp.GetClientArea();
        if ((displayRect.GetLeft() + displayRect.GetWidth()) < rect.GetLeft()) rect.SetLeft(displayRect.GetLeft());
        if ((displayRect.GetLeft() + displayRect.GetWidth()) < rect.GetRight()) rect.SetRight(displayRect.GetRight());
        if ((displayRect.GetTop() + displayRect.GetHeight()) < rect.GetTop()) rect.SetTop(displayRect.GetTop());
        if ((displayRect.GetTop() + displayRect.GetHeight()) < rect.GetBottom()) rect.SetBottom(displayRect.GetBottom());
    }

    SetSize(rect);
}

void MainFrame::SaveWindowState() {
    DoCheckCurrentLayoutForChanges(false);

    /*首先删除所有以前的布局,否则如果新的布局数量小于以前的,它们可能会保留,因为只有第一个布局将被覆盖.*/
    wxArrayString subs = Manager::Get()->GetConfigManager(_T("app"))->EnumerateSubPaths(_T("/main_frame/layout"));
    for (size_t i = 0; i < subs.GetCount(); ++i) {
        Manager::Get()->GetConfigManager(_T("app"))->DeleteSubPath(_T("/main_frame/layout/") + subs[i]);
    }

    int count = 0;
    for (LayoutViewsMap::iterator it = m_LayoutViews.begin(); it != m_LayoutViews.end(); ++it) {
        if (it->first.IsEmpty()) {
            continue;
        }
        ++count;
        wxString key = wxString::Format(_T("/main_frame/layout/view%d/"), count);
        Manager::Get()->GetConfigManager(_T("app"))->Write(key + _T("name"), it->first);
        Manager::Get()->GetConfigManager(_T("app"))->Write(key + _T("data"), it->second);

        if (!m_LayoutMessagePane[it->first].IsEmpty())
            Manager::Get()->GetConfigManager(_T("app"))->Write(key + _T("dataMessagePane"), m_LayoutMessagePane[it->first]);
    }

    /*保存管理器和选定页的消息*/
    if (m_pPrjManUI->GetNotebook()) {
        int selection = m_pPrjManUI->GetNotebook()->GetSelection();
        Manager::Get()->GetConfigManager(_T("app"))->Write(_T("/main_frame/layout/left_block_selection"), selection);
    }
    Manager::Get()->GetConfigManager(_T("app"))->Write(_T("/main_frame/layout/bottom_block_selection"), m_pInfoPane->GetSelection());

    /*保存显示,窗口大小和位置*/
    Manager::Get()->GetConfigManager(_T("app"))->Write(_T("/main_frame/layout/display"), wxDisplay::GetFromWindow(this));
    if (!IsMaximized() && !IsIconized()) {
        Manager::Get()->GetConfigManager(_T("app"))->Write(_T("/main_frame/layout/left"), GetPosition().x);
        Manager::Get()->GetConfigManager(_T("app"))->Write(_T("/main_frame/layout/top"), GetPosition().y);
        Manager::Get()->GetConfigManager(_T("app"))->Write(_T("/main_frame/layout/width"), GetSize().x);
        Manager::Get()->GetConfigManager(_T("app"))->Write(_T("/main_frame/layout/height"), GetSize().y);
    }
    Manager::Get()->GetConfigManager(_T("app"))->Write(_T("/main_frame/layout/maximized"), IsMaximized());
}

void MainFrame::LoadViewLayout(const wxString &name, bool isTemp) {
    if (m_LastLayoutName != name && !DoCheckCurrentLayoutForChanges(true)){return;}
    m_LastLayoutIsTemp = isTemp;
    wxString layout = m_LayoutViews[name];
    wxString layoutMP = m_LayoutMessagePane[name];
    if (layoutMP.IsEmpty()){layoutMP = m_LayoutMessagePane[gDefaultLayout];}
    if (layout.IsEmpty()) {
        layout = m_LayoutViews[gDefaultLayout];
        SaveViewLayout(name, layout, layoutMP, false);
        DoSelectLayout(name);
    } else {
        DoSelectLayout(name);
    }

    /*第一次加载MessagePane的tab次序,以便LoadPerspective可以还原最后选定的选项卡*/
    m_pInfoPane->LoadTabOrder(layoutMP);
    m_LayoutManager.LoadPerspective(layout, false);
    DoUpdateLayout();
    m_PreviousLayoutName = m_LastLayoutName;
    m_LastLayoutName = name;
    m_LastLayoutData = layout;
    m_LastMessagePaneLayoutData = layoutMP;
    CodeBlocksLayoutEvent evt(cbEVT_SWITCHED_VIEW_LAYOUT);
    evt.layout = name;
    Manager::Get()->ProcessEvent(evt);
}

void MainFrame::SaveViewLayout(const wxString &name, const wxString &layout, const wxString &layoutMP, bool select) {
    if (name.IsEmpty()){return;}
    m_LayoutViews[name] = layout;
    m_LayoutMessagePane[name] = layoutMP;
    wxMenu *viewLayouts = nullptr;
    GetMenuBar()->FindItem(idViewLayoutSave, &viewLayouts);
    if (viewLayouts && viewLayouts->FindItem(name) == wxNOT_FOUND) {
        int id = wxNewId();
        viewLayouts->InsertCheckItem(viewLayouts->GetMenuItemCount() - 3, id, name, wxString::Format(wxT("切换到%s透视图"), name.wx_str()));
        Connect(id, wxEVT_COMMAND_MENU_SELECTED,
                (wxObjectEventFunction) (wxEventFunction) (wxCommandEventFunction) &MainFrame::OnViewLayout);
        m_PluginIDsMap[id] = name;
    }
    if (select) {
        DoSelectLayout(name);
        m_LastLayoutName = name;
    }
}

bool MainFrame::LayoutDifferent(const wxString &layout1, const wxString &layout2, const wxString &delimiter) {
    wxStringTokenizer strTok;
    unsigned long j;

    strTok.SetString(layout1, delimiter);
    wxArrayString arLayout1;
    while (strTok.HasMoreTokens()) {
        wxStringTokenizer strTokColon(strTok.GetNextToken(), _T(";"));
        while (strTokColon.HasMoreTokens()) {
            wxString theToken = strTokColon.GetNextToken();
            if (theToken.StartsWith(_T("state="))) {
                theToken = theToken.Right(theToken.Len() - wxString(_T("state=")).Len());
                theToken.ToULong(&j);
                /*我们过滤掉隐藏/显示状态*/
                theToken = wxString::Format(_("state=%lu"), j & wxAuiPaneInfo::optionHidden);
            }
            arLayout1.Add(theToken);
        }
    }

    strTok.SetString(layout2, delimiter);
    wxArrayString arLayout2;
    while (strTok.HasMoreTokens()) {
        wxStringTokenizer strTokColon(strTok.GetNextToken(), _T(";"));
        while (strTokColon.HasMoreTokens()) {
            wxString theToken = strTokColon.GetNextToken();
            if (theToken.StartsWith(_T("state="))) {
                theToken = theToken.Right(theToken.Len() - wxString(_T("state=")).Len());
                theToken.ToULong(&j);
                /*我们过滤掉隐藏/显示状态*/
                theToken = wxString::Format(_("state=%lu"), j & wxAuiPaneInfo::optionHidden);
            }
            arLayout2.Add(theToken);
        }
    }

    arLayout1.Sort();
    arLayout2.Sort();

    return arLayout1 != arLayout2;
}

bool MainFrame::LayoutMessagePaneDifferent(const wxString &layout1, const wxString &layout2, bool checkSelection) {
    wxStringTokenizer strTok;
    wxArrayString arLayout1;
    wxArrayString arLayout2;

    strTok.SetString(layout1.BeforeLast('|'), _T(";"));
    while (strTok.HasMoreTokens()) {
        arLayout1.Add(strTok.GetNextToken());
    }

    strTok.SetString(layout2.BeforeLast('|'), _T(";"));
    while (strTok.HasMoreTokens()) {
        arLayout2.Add(strTok.GetNextToken());
    }

    if (checkSelection) {
        arLayout1.Add(layout1.AfterLast('|'));
        arLayout2.Add(layout2.AfterLast('|'));
    }
    arLayout1.Sort();
    arLayout2.Sort();

    return arLayout1 != arLayout2;
}

bool MainFrame::DoCheckCurrentLayoutForChanges(bool canCancel) {
    DoFixToolbarsLayout();
    wxString lastlayout = m_LayoutManager.SavePerspective();
    wxString lastmessagepanelayout = m_pInfoPane->SaveTabOrder();
    if (!m_LastLayoutName.IsEmpty() &&
        (LayoutDifferent(lastlayout, m_LastLayoutData) ||
         LayoutMessagePaneDifferent(lastmessagepanelayout, m_LastMessagePaneLayoutData, Manager::Get()->GetConfigManager(_T("message_manager"))->ReadBool(_T("/save_selection_change_in_mp"), true)))) {
        /*错误18970的部分修复(修复不完整,无法阻止用户保存"rtCANCEL")*/
        AnnoyingDialog dlg(wxT("布局被改变"),
                           wxString::Format(wxT("透视图'%s'已更改.你想保存它吗?"), m_LastLayoutName.wx_str()),
                           wxART_QUESTION,
                           canCancel ? AnnoyingDialog::YES_NO_CANCEL : AnnoyingDialog::YES_NO,
                           canCancel ? AnnoyingDialog::rtYES : AnnoyingDialog::rtSAVE_CHOICE);
        switch (dlg.ShowModal()) {
            case AnnoyingDialog::rtYES:
                SaveViewLayout(m_LastLayoutName, lastlayout, lastmessagepanelayout, false);
                break;
            case AnnoyingDialog::rtCANCEL:
                DoSelectLayout(m_LastLayoutName);
                return false;
            default:
                break;
        }
    }
    return true;
}

void MainFrame::DoFixToolbarsLayout() {
    /*因为用户可能会改变工具栏图标的大小,我们必须迎合它...*/
    wxAuiPaneInfoArray &panes = m_LayoutManager.GetAllPanes();
    for (size_t i = 0; i < panes.GetCount(); ++i) {
        wxAuiPaneInfo &info = panes[i];
        if (info.state & wxAuiPaneInfo::optionToolbar) {
            info.best_size = info.window->GetBestSize();
            info.floating_size = wxDefaultSize;
        }
    }
}

void MainFrame::DoSelectLayout(const wxString &name) {
    wxMenu *viewLayouts = nullptr;
    GetMenuBar()->FindItem(idViewLayoutSave, &viewLayouts);
    if (viewLayouts) {
        wxMenuItemList &items = viewLayouts->GetMenuItems();
        for (size_t i = 0; i < items.GetCount(); ++i) {
            if (!items[i]->IsCheckable())
                continue;
#if wxCHECK_VERSION(3, 0, 0)
            items[i]->Check(items[i]->GetItemLabel().IsSameAs(name));
#else
            items[i]->Check(items[i]->GetText().IsSameAs(name));
#endif
        }

        if (!m_LastLayoutIsTemp)
            Manager::Get()->GetConfigManager(_T("app"))->Write(_T("/main_frame/layout/default"), name);
    }
}

void MainFrame::DoAddPluginStatusField(cbPlugin *plugin) {
    cbStatusBar *sbar = (cbStatusBar *) GetStatusBar();
    if (!sbar){return;}
    plugin->CreateStatusField(sbar);
    sbar->AdjustFieldsSize();
}

inline void InitToolbar(wxToolBar *tb) {
    tb->SetInitialSize();
}

ToolbarInfo MainFrame::DoAddPluginToolbar(cbPlugin *plugin) {
    ToolbarInfo info;
    info.toolbar = Manager::Get()->CreateEmptyToolbar();
    if (plugin->BuildToolBar(info.toolbar)) {
        info.priority = plugin->GetToolBarPriority();
        SetToolBar(nullptr);
        InitToolbar(info.toolbar);

        /*为工具栏添加"视图"->"工具栏"菜单项*/
        wxMenu *viewToolbars = nullptr;
        GetMenuBar()->FindItem(idViewToolMain, &viewToolbars);
        if (viewToolbars) {
            wxMenuItem *item = AddPluginInMenus(viewToolbars, plugin,
                                                (wxObjectEventFunction) (wxEventFunction) (wxCommandEventFunction) &MainFrame::OnToggleBar,
                                                -1, true);
            if (item) {
                item->Check(true);
                m_PluginsTools[plugin] = info.toolbar;
            }
        }

        const PluginInfo *pluginInfo = Manager::Get()->GetPluginManager()->GetPluginInfo(plugin);

        /*if (!pluginInfo){cbThrow(wxT("没有插件信息?!?"));}*/

        info.paneInfo.Name(pluginInfo->name + _T("Toolbar")).Caption(pluginInfo->title + wxT(" 工具栏"));
    } else {
        delete info.toolbar;
        info.toolbar = nullptr;
    }
    return info;
}

void MainFrame::DoAddPlugin(cbPlugin *plugin) {
    //Manager::Get()->GetLogManager()->DebugLog(wxT("正在添加插件: %s"), plugin->GetInfo()->name.wx_str());
    AddPluginInHelpPluginsMenu(plugin);
    if (plugin->GetType() == ptTool) {
        AddPluginInPluginsMenu(plugin);
    }else {
        /*为其他插件提供菜单和工具栏空间*/
        try {
            wxMenuBar *mbar = GetMenuBar();
            plugin->BuildMenu(mbar);
        }
        catch (cbException &e) {
            e.ShowErrorMessage();
        }
        /*不要在插件的初始加载过程中加载工具栏,这段代码应该只在从"插件"->"管理器"加载单个插件时执行...窗口.*/
        if (!m_ScanningForPlugins) {
            /*创建插件的工具栏(如果有).*/
            const ToolbarInfo &toolbarInfo = DoAddPluginToolbar(plugin);
            if (toolbarInfo.toolbar) {
                /*将新工具栏放置在工具栏面板的底部.如果最后一行有足够的空间,请尝试重用它,否则将新工具栏放置在新行.*/
                int row = 0;
                const wxAuiPaneInfoArray &panes = m_LayoutManager.GetAllPanes();
                for (size_t ii = 0; ii < panes.GetCount(); ++ii) {
                    const wxAuiPaneInfo &info = panes[ii];
                    if (info.IsToolbar())
                        row = std::max(row, info.dock_row);
                }
                int minX = 100000, maxX = -100000;
                int position = 0;
                for (size_t ii = 0; ii < panes.GetCount(); ++ii) {
                    const wxAuiPaneInfo &info = panes[ii];
                    if (info.IsToolbar() && info.dock_row == row && info.window) {
                        const wxPoint &pt = info.window->GetPosition();
                        minX = std::min(minX, pt.x + info.window->GetSize().x);
                        maxX = std::max(maxX, pt.x + info.window->GetSize().x);
                        position = std::max(position, info.dock_pos);
                    }
                }
                if (maxX + toolbarInfo.toolbar->GetSize().x <= GetSize().x)
                    position++;
                else {
                    row++;
                    position = 0;
                }
                wxAuiPaneInfo paneInfo(toolbarInfo.paneInfo);
                m_LayoutManager.AddPane(toolbarInfo.toolbar, paneInfo.ToolbarPane().Top().Row(row).Position(position));
                /*添加鼠标右键单击的事件处理程序*/
                toolbarInfo.toolbar->Connect(wxID_ANY, wxEVT_COMMAND_TOOL_RCLICKED,
                                             wxCommandEventHandler(MainFrame::OnToolBarRightClick), NULL, this);

                DoUpdateLayout();
            }
        }
        DoAddPluginStatusField(plugin);
    }
}

bool MainFrame::Open(const wxString &filename, bool addToHistory) {
    wxFileName fn(filename);
    /*非常重要,这样两个同名的文件就不会加载两次*/
    fn.Normalize();
    wxString name = fn.GetFullPath();
    Manager::Get()->GetLogManager()->DebugLog(wxT("正在打开文件 ") + name);
    bool ret = OpenGeneric(name, addToHistory);
    return ret;
}

wxString MainFrame::ShowOpenFileDialog(const wxString &caption, const wxString &filter) {
    wxFileDialog dlg(this,
                     caption,
                     wxEmptyString,
                     wxEmptyString,
                     filter,
                     wxFD_OPEN | compatibility::wxHideReadonly);
    wxString sel;
    PlaceWindow(&dlg);
    if (dlg.ShowModal() == wxID_OK)
        sel = dlg.GetPath();
    return sel;
}

/*通用打开函数*/
bool MainFrame::OpenGeneric(const wxString &filename, bool addToHistory) {
    if (filename.IsEmpty()){return false;}

    wxFileName fname(filename);
    fname.ClearExt();
    fname.SetExt(_T("cbp"));
    switch (FileTypeOf(filename)) {
        /*工作区*/
        case ftCodeBlocksWorkspace:
            /*确认它与已打开的不相同*/
            if (filename == Manager::Get()->GetProjectManager()->GetWorkspace()->GetFilename()) {
                return true;
            }else{
                if (DoCloseCurrentWorkspace()) {
                    /*加载工作区可能需要一些时间->显示沙漏*/
                    wxBusyCursor wait;
                    /*隐藏"从此处开始"页,以便我们可以使用整个选项卡范围*/
                    ShowHideStartPage(true);
                    bool ret = Manager::Get()->GetProjectManager()->LoadWorkspace(filename);
                    if (!ret) {
                        /*如果加载失败,根据设置显示/隐藏"从此处开始"页*/
                        ShowHideStartPage();
                    }else if (addToHistory) {
                        m_projectsHistory.AddToHistory(Manager::Get()->GetProjectManager()->GetWorkspace()->GetFilename());
                    }
                    return ret;
                } else {
                    return false;
                }
            }
            break;

            /*项目*/
        case ftCodeBlocksProject: {
            /*检查项目是否存在于当前工作区中*/
            cbProject *prj = Manager::Get()->GetProjectManager()->IsOpen(fname.GetFullPath());
            if (!prj) {
                /*加载工作区可能需要一些时间->显示沙漏*/
                wxBusyCursor wait;
                return DoOpenProject(filename, addToHistory);
            } else {
                /*注(Morten#1#):此处的消息将阻止批生成工作,并且有时即使正确也会显示*/
                Manager::Get()->GetProjectManager()->SetProject(prj, false);
                return true;
            }
        }
            /*源代码[头文件/源码/模板文件/资源文件]*/
        case ftHeader:
        case ftSource:
        case ftTemplateSource:
        case ftResource:
            return DoOpenFile(filename, addToHistory);
            /*对于所有其他文件,请向MIME插件请求合适的处理程序*/
        default: {
            cbMimePlugin *plugin = Manager::Get()->GetPluginManager()->GetMIMEHandlerForFile(filename);
            /*警告用户"文件扩展处理程序"已禁用*/
            if (!plugin) {
                cbMessageBox(wxT("无法打开文件 ") + filename + wxT(",\n因为找不到扩展处理程序."), wxT("错误"), wxICON_ERROR);
                return false;
            }
            if (plugin->OpenFile(filename) == 0) {
                m_filesHistory.AddToHistory(filename);
                return true;
            }
            return false;
        }
    }
    return true;
}

bool MainFrame::DoOpenProject(const wxString &filename, bool addToHistory) {
    if (!wxFileExists(filename)) {
        cbMessageBox(wxT("项目文件不存在..."), wxT("错误"), wxICON_ERROR);
        return false;
    }

    /*隐藏"从此处开始"页,以便我们可以使用整个选项卡范围*/
    ShowHideStartPage(true);
    cbProject *prj = Manager::Get()->GetProjectManager()->LoadProject(filename, true);
    if (prj) {
        if (addToHistory)
            m_projectsHistory.AddToHistory(prj->GetFilename());
        return true;
    }

    /*如果加载失败,根据设置显示/隐藏"从此处开始"页*/
    ShowHideStartPage();
    return false;
}

bool MainFrame::DoOpenFile(const wxString &filename, bool addToHistory) {
    cbEditor *ed = Manager::Get()->GetEditorManager()->Open(filename);
    if (ed) {
        /*Cryogen于24/3/10 打开后激活编辑器.错误14087的部分修复.*/
        ed->Activate();
        if (addToHistory) {
            m_filesHistory.AddToHistory(ed->GetFilename());
        }
        return true;
    }
    return false;
}

bool MainFrame::DoCloseCurrentWorkspace() {
    return Manager::Get()->GetProjectManager()->CloseWorkspace();
}

/*创建状态栏*/
void MainFrame::DoCreateStatusBar() {
    wxClientDC dc(this);
    wxFont font = dc.GetFont();
    int h;
    size_t num = 0;
    /*最大16*/
    wxCoord widths[16];
    /*主字段*/
    widths[num++] = -1;

    dc.GetTextExtent(wxT(" 高亮按钮 "), &widths[num++], &h);
    dc.GetTextExtent(wxT(" Windows (CR+LF) "), &widths[num++], &h);
    dc.GetTextExtent(wxT(" WINDOWS-1252 "), &widths[num++], &h);
    dc.GetTextExtent(wxT(" 行 12345, 列 123, 位置 123456 "), &widths[num++], &h);
    dc.GetTextExtent(wxT(" 覆盖 "), &widths[num++], &h);
    dc.GetTextExtent(wxT(" 被修改 "), &widths[num++], &h);
    dc.GetTextExtent(wxT(" 读/写 "), &widths[num++], &h);
    dc.GetTextExtent(wxT(" 配置名 "), &widths[num++], &h);

    wxStatusBar *sb = CreateStatusBar(num);
    if (!sb) {
        return;
    }

    SetStatusWidths(num, widths);

    /*突出显示按钮*/
    wxRect rect;
    if (sb->GetFieldRect(1, rect)) {
        m_pHighlightButton = new wxButton(sb, idHighlightButton, wxT("高亮按钮"), wxDefaultPosition, wxDefaultSize,wxBORDER_NONE | wxBU_LEFT | wxBU_EXACTFIT);
        /*调整状态栏高度以适合按钮.这对wx3.x构建的影响大于wx2.8构建.至少在wxGTK上.*/
        const int height = std::max(sb->GetMinHeight(), m_pHighlightButton->GetClientSize().GetHeight());
        sb->SetMinHeight(height);
    }
}

/*
仅当按钮的标签确实更改时才更改.
这是用于状态栏按钮,因为如果我们总是设置标签,在编辑器中滚动时会闪烁.
我观察到wxGTK上的闪烁,不知道其他端口上是否存在.
 */
static void changeButtonLabel(wxButton &button, const wxString &text) {
    if (text != button.GetLabel()){button.SetLabel(text);}
}

/*更新状态栏*/
void MainFrame::DoUpdateStatusBar() {
    if (!GetStatusBar()){return;}

    cbEditor *ed = Manager::Get()->GetEditorManager()->GetBuiltinActiveEditor();
    wxString personality(Manager::Get()->GetPersonalityManager()->GetPersonality());
    if (ed) {
        cbStyledTextCtrl *const control = ed->GetControl();

        int panel = 0;
        int pos = control->GetCurrentPos();
        wxString msg;
        SetStatusText(ed->GetFilename(), panel++);

        if (m_pHighlightButton) {
            EditorColourSet *colour_set = Manager::Get()->GetEditorManager()->GetColourSet();
            if (colour_set)
                changeButtonLabel(*m_pHighlightButton, colour_set->GetLanguageName(ed->GetLanguage()));
            else
                changeButtonLabel(*m_pHighlightButton, wxEmptyString);
        }
        /*结束符(EOL)模式*/
        panel++;
        switch (control->GetEOLMode()) {
            case wxSCI_EOL_CRLF:
                msg = _T("Windows (CR+LF)");
                break;
            case wxSCI_EOL_CR:
                msg = _T("Mac (CR)");
                break;
            case wxSCI_EOL_LF:
                msg = _T("Unix (LF)");
                break;
            default:
                break;
        }
        SetStatusText(msg, panel++);
        SetStatusText(ed->GetEncodingName(), panel++);
        msg.Printf(wxT("行 %d, 列 %d, 位置 %d"), control->GetCurrentLine() + 1, control->GetColumn(pos) + 1, pos);
        SetStatusText(msg, panel++);
        SetStatusText(control->GetOvertype() ? wxT("覆盖") : wxT("插入"), panel++);
#if wxCHECK_VERSION(3, 0, 0)
        SetStatusText(ed->GetModified() ? wxT("修改") : wxT(""), panel++);
#else
        SetStatusText(ed->GetModified() ? _("Modified") : wxEmptyString, panel++);
#endif
        SetStatusText(control->GetReadOnly() ? wxT("只读") : wxT("读/写"), panel++);
        SetStatusText(personality, panel++);
    } else {
        int panel = 0;
        EditorBase *eb = Manager::Get()->GetEditorManager()->GetActiveEditor();
        if (eb) {
            SetStatusText(eb->GetFilename(), panel++);
        }else {
            SetStatusText(wxString::Format(wxT("欢迎使用%s!"), appglobals::AppName.wx_str()), panel++);
        }
        if (m_pHighlightButton) {
            changeButtonLabel(*m_pHighlightButton, wxEmptyString);
        }
        panel++;

        SetStatusText(wxEmptyString, panel++);
        SetStatusText(wxEmptyString, panel++);
        SetStatusText(wxEmptyString, panel++);
        SetStatusText(wxEmptyString, panel++);
        SetStatusText(wxEmptyString, panel++);
        SetStatusText(wxEmptyString, panel++);
        SetStatusText(personality, panel++);
    }
    /*重画状态条*/
    GetStatusBar()->Fit();

}

/*更新编辑器样式*/
void MainFrame::DoUpdateEditorStyle(cbAuiNotebook *target, const wxString &prefix, long defaultStyle) {
    if (!target){return;}

    ConfigManager *cfg = Manager::Get()->GetConfigManager(_T("app"));
    target->SetTabCtrlHeight(1);

    long nbstyle = cfg->ReadInt(_T("/environment/tabs_style"), 0);
   /*选项卡样式*/
    switch (nbstyle) {
        case 1:
            /*简单风格*/
            target->SetArtProvider(new wxAuiSimpleTabArt());
            break;

        case 2:
            /*VC 7.1 风格*/
            target->SetArtProvider(new NbStyleVC71());
            break;

        case 3:
            /*Firefox 2 风格*/
            target->SetArtProvider(new NbStyleFF2());
            break;

        default:
            /*默认风格*/
#if defined(__WXGTK__) && (USE_GTK_NOTEBOOK) && !wxCHECK_VERSION(3, 0, 0)
            target->SetArtProvider(new NbStyleGTK());
#else
            target->SetArtProvider(new wxAuiDefaultTabArt());
#endif
            break;
    }

    target->SetTabCtrlHeight(-1);

    nbstyle = defaultStyle;
    if (cfg->ReadBool(_T("/environment/") + prefix + _T("_tabs_bottom")))
        nbstyle |= wxAUI_NB_BOTTOM;

    if (cfg->ReadBool(_T("/environment/tabs_list")))
        nbstyle |= wxAUI_NB_WINDOWLIST_BUTTON;

    target->SetWindowStyleFlag(nbstyle);
}

/*更新编辑器风格*/
void MainFrame::DoUpdateEditorStyle() {
    long style = wxAUI_NB_TAB_SPLIT | wxAUI_NB_TAB_MOVE | wxAUI_NB_SCROLL_BUTTONS | wxAUI_NB_MIDDLE_CLICK_CLOSE;
    long closestyle = Manager::Get()->GetConfigManager(_T("app"))->ReadInt(_T("/environment/tabs_closestyle"), 0);

    /*关闭选项卡*/
    switch (closestyle) {
        case 1:
            /*当前选项卡*/
            style |= wxAUI_NB_CLOSE_ON_ACTIVE_TAB;
            break;

        case 2:
            /*右边*/
            style |= wxAUI_NB_CLOSE_BUTTON;
            break;

        default:
            /*全部选项卡(默认)*/
            style |= wxAUI_NB_CLOSE_ON_ALL_TABS;
            break;
    }

    cbAuiNotebook *an = Manager::Get()->GetEditorManager()->GetNotebook();

    DoUpdateEditorStyle(an, _T("editor"), style | wxNO_FULL_REPAINT_ON_RESIZE | wxCLIP_CHILDREN);
    if (Manager::Get()->GetConfigManager(_T("app"))->ReadBool(_T("/environment/hide_editor_tabs"), false))
        an->SetTabCtrlHeight(0);

    an = m_pInfoPane;
    DoUpdateEditorStyle(an, _T("infopane"), style);

    an = m_pPrjManUI->GetNotebook();
    DoUpdateEditorStyle(an, _T("project"), wxAUI_NB_SCROLL_BUTTONS | wxAUI_NB_TAB_MOVE);
}

void MainFrame::DoUpdateLayoutColours() {
    ConfigManager *cfg = Manager::Get()->GetConfigManager(_T("app"));
    wxAuiDockArt *art = m_LayoutManager.GetArtProvider();

#ifndef __WXGTK__
    m_LayoutManager.SetFlags(wxAUI_MGR_DEFAULT | wxAUI_MGR_ALLOW_ACTIVE_PANE | wxAUI_MGR_TRANSPARENT_DRAG);
#else
    /*未定义__WXGTK__ 解决wxWidgets错误的方法,当浮动窗口停靠并启用复合效果时,该错误会导致C::B崩溃*/
    m_LayoutManager.SetFlags((wxAUI_MGR_DEFAULT | wxAUI_MGR_ALLOW_ACTIVE_PANE | wxAUI_MGR_TRANSPARENT_DRAG | wxAUI_MGR_VENETIAN_BLINDS_HINT) & ~wxAUI_MGR_TRANSPARENT_HINT);
#endif // #ifndef __WXGTK__

    art->SetMetric(wxAUI_DOCKART_PANE_BORDER_SIZE, cfg->ReadInt(_T("/environment/aui/border_size"), art->GetMetric(wxAUI_DOCKART_PANE_BORDER_SIZE)));
    art->SetMetric(wxAUI_DOCKART_SASH_SIZE, cfg->ReadInt(_T("/environment/aui/sash_size"), art->GetMetric(wxAUI_DOCKART_SASH_SIZE)));
    art->SetMetric(wxAUI_DOCKART_CAPTION_SIZE, cfg->ReadInt(_T("/environment/aui/caption_size"), art->GetMetric(wxAUI_DOCKART_CAPTION_SIZE)));
    art->SetColour(wxAUI_DOCKART_ACTIVE_CAPTION_COLOUR, cfg->ReadColour(_T("/environment/aui/active_caption_colour"), art->GetColour(wxAUI_DOCKART_ACTIVE_CAPTION_COLOUR)));
    art->SetColour(wxAUI_DOCKART_ACTIVE_CAPTION_GRADIENT_COLOUR, cfg->ReadColour(_T("/environment/aui/active_caption_gradient_colour"), art->GetColour(wxAUI_DOCKART_ACTIVE_CAPTION_GRADIENT_COLOUR)));
    art->SetColour(wxAUI_DOCKART_ACTIVE_CAPTION_TEXT_COLOUR, cfg->ReadColour(_T("/environment/aui/active_caption_text_colour"), art->GetColour(wxAUI_DOCKART_ACTIVE_CAPTION_TEXT_COLOUR)));
    art->SetColour(wxAUI_DOCKART_INACTIVE_CAPTION_COLOUR, cfg->ReadColour(_T("/environment/aui/inactive_caption_colour"), art->GetColour(wxAUI_DOCKART_INACTIVE_CAPTION_COLOUR)));
    art->SetColour(wxAUI_DOCKART_INACTIVE_CAPTION_GRADIENT_COLOUR, cfg->ReadColour(_T("/environment/aui/inactive_caption_gradient_colour"), art->GetColour(wxAUI_DOCKART_INACTIVE_CAPTION_GRADIENT_COLOUR)));
    art->SetColour(wxAUI_DOCKART_INACTIVE_CAPTION_TEXT_COLOUR, cfg->ReadColour(_T("/environment/aui/inactive_caption_text_colour"), art->GetColour(wxAUI_DOCKART_INACTIVE_CAPTION_TEXT_COLOUR)));

    DoUpdateLayout();
}

void MainFrame::DoUpdateLayout() {
    if (!m_StartupDone){return;}
    DoFixToolbarsLayout();
    m_LayoutManager.Update();
}

void MainFrame::DoUpdateAppTitle() {
    EditorBase *ed = Manager::Get()->GetEditorManager() ? Manager::Get()->GetEditorManager()->GetActiveEditor() : nullptr;
    cbProject *prj = nullptr;
    if (ed && ed->IsBuiltinEditor()) {
        ProjectFile *prjf = ((cbEditor *) ed)->GetProjectFile();
        if (prjf)
            prj = prjf->GetParentProject();
    } else
        prj = Manager::Get()->GetProjectManager() ? Manager::Get()->GetProjectManager()->GetActiveProject() : nullptr;
    wxString projname;
    wxString edname;
    wxString fulltitle;
    if (ed || prj) {
        if (prj) {
            if (Manager::Get()->GetProjectManager()->GetActiveProject() == prj)
                projname = wxString(_T(" [")) + prj->GetTitle() + _T("]");
            else
                projname = wxString(_T(" (")) + prj->GetTitle() + _T(")");
        }
        if (ed)
            edname = ed->GetTitle();
        fulltitle = edname + projname;
        if (!fulltitle.IsEmpty())
            fulltitle.Append(_T(" - "));
    }
    fulltitle.Append(appglobals::AppName);
    fulltitle.Append(_T(" "));
    fulltitle.Append(appglobals::AppVersion);
    SetTitle(fulltitle);
}

void MainFrame::ShowHideStartPage(bool forceHasProject, int forceState) {
    if (Manager::IsBatchBuild()){return;}
    /*我们使用forceHasProject参数,因为当项目打开时,EVT_PROJECT_OPEN事件在ProjectManager::GetProjects()和ProjectManager::GetActiveProject()更新之前被触发..*/
    if (m_InitiatedShutdown) {
        EditorBase *sh = Manager::Get()->GetEditorManager()->GetEditor(g_StartHereTitle);
        if (sh){sh->Destroy();}
        return;
    }

    bool show = !forceHasProject &&
                Manager::Get()->GetProjectManager()->GetProjects()->GetCount() == 0 &&
                Manager::Get()->GetConfigManager(_T("app"))->ReadBool(_T("/environment/start_here_page"), true);

    if (forceState < 0){show = false;}
    if (forceState > 0){show = true;}

    EditorBase *sh = Manager::Get()->GetEditorManager()->GetEditor(g_StartHereTitle);
    if (show) {
        if (!sh) {
            sh = new StartHerePage(this, m_projectsHistory, m_filesHistory,
                                   Manager::Get()->GetEditorManager()->GetNotebook());
        } else {
            static_cast<StartHerePage *>(sh)->Reload();
        }
    } else if (!show && sh) {
        sh->Destroy();
    }

    DoUpdateAppTitle();
}

/*显示/隐藏脚本控制台*/
void MainFrame::ShowHideScriptConsole() {
    if (Manager::IsBatchBuild()){return;}
    bool isVis = IsWindowReallyShown(m_pScriptConsole);
    CodeBlocksDockEvent evt(isVis ? cbEVT_HIDE_DOCK_WINDOW : cbEVT_SHOW_DOCK_WINDOW);
    evt.pWindow = m_pScriptConsole;
    Manager::Get()->ProcessEvent(evt);
}

/*起始页超链接*/
void MainFrame::OnStartHereLink(wxCommandEvent &event) {
    wxCommandEvent evt;
    evt.SetId(idFileNewProject);
    wxString link = event.GetString();
    if (link.IsSameAs(_T("CB_CMD_NEW_PROJECT"))) {
        OnFileNewWhat(evt);
    } else if (link.IsSameAs(_T("CB_CMD_OPEN_PROJECT"))){
        DoOnFileOpen(true);
    }else if (link.StartsWith(_T("CB_CMD_OPEN_HISTORY_"))) {
        RecentItemsList *recent;
        recent = link.StartsWith(_T("CB_CMD_OPEN_HISTORY_PROJECT_")) ? &m_projectsHistory : &m_filesHistory;
        unsigned long item;
        link.AfterLast(_T('_')).ToULong(&item);
        --item;
        const wxString &filename = recent->GetHistoryFile(item);
        if (!filename.empty()) {
            if (!OpenGeneric(filename, true))
                recent->AskToRemoveFileFromHistory(item);
        }
    } else if (link.StartsWith(_T("CB_CMD_DELETE_HISTORY_"))) {
        RecentItemsList *recent;
        recent = link.StartsWith(_T("CB_CMD_DELETE_HISTORY_PROJECT_")) ? &m_projectsHistory : &m_filesHistory;
        unsigned long item;
        link.AfterLast(_T('_')).ToULong(&item);
        --item;
        recent->AskToRemoveFileFromHistory(item, false);
    } else if (link.IsSameAs(_T("CB_CMD_TIP_OF_THE_DAY"))){
        ShowTips(true);
    }
}

/*初始化最近打开文件历史记录*/
void MainFrame::InitializeRecentFilesHistory() {
    m_filesHistory.Initialize();
    m_projectsHistory.Initialize();
}

void MainFrame::TerminateRecentFilesHistory() {
    m_filesHistory.TerminateHistory();
    m_projectsHistory.TerminateHistory();
}

/*编辑器描述*/
wxString MainFrame::GetEditorDescription(EditorBase *eb) {
    wxString descr = wxEmptyString;
    cbProject *prj = NULL;
    if (eb && eb->IsBuiltinEditor()) {
        ProjectFile *prjf = ((cbEditor *) eb)->GetProjectFile();
        if (prjf)
            prj = prjf->GetParentProject();
    } else
        prj = Manager::Get()->GetProjectManager() ? Manager::Get()->GetProjectManager()->GetActiveProject() : nullptr;
    if (prj) {
        descr = wxString(wxT("项目: ")) + wxT("<b>") + prj->GetTitle() + wxT("</b>");
        if (Manager::Get()->GetProjectManager()->GetActiveProject() == prj) {
            descr += wxString(wxT(" (激活)"));
        }
        descr += wxString(wxT("<br>"));
    }
    if (eb){descr += eb->GetFilename();}
    return descr;
}

/*---------------------事件处理程序----------------------------------------*/

/*插件执行菜单*/
void MainFrame::OnPluginsExecuteMenu(wxCommandEvent &event) {
    wxString pluginName = m_PluginIDsMap[event.GetId()];
    if (!pluginName.IsEmpty())
        Manager::Get()->GetPluginManager()->ExecutePlugin(pluginName);
    else
        Manager::Get()->GetLogManager()->DebugLog(wxString::Format(wxT("找不到ID为 %d 的插件"), event.GetId()));
}

/*插件帮助菜单*/
void MainFrame::OnHelpPluginMenu(wxCommandEvent &event) {
    wxString pluginName = m_PluginIDsMap[event.GetId()];
    if (!pluginName.IsEmpty()) {
        const PluginInfo *pi = Manager::Get()->GetPluginManager()->GetPluginInfo(pluginName);
        if (!pi) {
            Manager::Get()->GetLogManager()->DebugLog(wxString::Format(wxT("没有 %s 插件信息"),pluginName.wx_str()));
            return;
        }
        dlgAboutPlugin dlg(this, pi);
        PlaceWindow(&dlg);
        dlg.ShowModal();
    } else {
        Manager::Get()->GetLogManager()->DebugLog(wxString::Format(wxT("找不到ID为 %d 的插件"), event.GetId()));
    }
}

void MainFrame::OnFileNewWhat(wxCommandEvent &event) {
    int id = event.GetId();
    if (id != idFileNewEmpty) {
         /*(新文件)基于向导*/
        TemplateOutputType tot = totProject;
        if (id == idFileNewProject) tot = totProject;
        else if (id == idFileNewTarget) tot = totTarget;
        else if (id == idFileNewFile) tot = totFiles;
        else if (id == idFileNewCustom) tot = totCustom;
        else if (id == idFileNewUser) tot = totUser;
        else return;

        wxString filename;
        cbProject *prj = TemplateManager::Get()->New(tot, &filename);

        /*验证打开的文件是否仍处于同步状态(新文件可能已覆盖现有文件)*/
        Manager::Get()->GetEditorManager()->CheckForExternallyModifiedFiles();

        /*如果两者都为空,则表示向导已失败*/
        if (!prj && filename.IsEmpty()){return;}

        /*发送新项目事件*/
        CodeBlocksEvent evtNew(cbEVT_PROJECT_NEW, 0, prj);
        Manager::Get()->GetPluginManager()->NotifyPlugins(evtNew);

        if (prj) {
            prj->Save();
            prj->SaveAllFiles();
        }

        if (!filename.IsEmpty()) {
            if (prj)
                m_projectsHistory.AddToHistory(filename);
            else
                m_filesHistory.AddToHistory(filename);
        }

        /*应分析创建的项目*/
        if (prj && tot == totProject)
        {
            CodeBlocksEvent evtOpen(cbEVT_PROJECT_OPEN, 0, prj);
            Manager::Get()->GetPluginManager()->NotifyPlugins(evtOpen);
        }
        return;
    }

    /*下面是新的空文件快捷方式代码*/
    cbProject *project = Manager::Get()->GetProjectManager()->GetActiveProject();
    if (project) {
        wxSetWorkingDirectory(project->GetBasePath());
    }
    cbEditor *ed = Manager::Get()->GetEditorManager()->New();
    /*如果在空文件上配置,则最初启动更改收集*/
    if (ed)
        ed->GetControl()->SetChangeCollection(Manager::Get()->GetConfigManager(_T("editor"))->ReadBool(_T("/margin/use_changebar"), true));

    if (ed && ed->IsOK())
        m_filesHistory.AddToHistory(ed->GetFilename());

    if (!ed || !project){return;}

    wxString oldname = ed->GetFilename();
    if (cbMessageBox(wxT("是否要将此新文件添加到活动项目中(必须先保存)?"),
                     wxT("将文件添加到项目"),
                     wxYES_NO | wxICON_QUESTION) == wxID_YES &&
        ed->SaveAs() && ed->IsOK()) {
        wxArrayInt targets;
        if (Manager::Get()->GetProjectManager()->AddFileToProject(ed->GetFilename(), project, targets) != 0) {
            ProjectFile *pf = project->GetFileByFilename(ed->GetFilename(), false);
            ed->SetProjectFile(pf);
            m_pPrjManUI->RebuildTree();
        }
    }

    /*验证打开的文件是否仍处于同步状态(新文件可能已覆盖现有文件)*/
    Manager::Get()->GetEditorManager()->CheckForExternallyModifiedFiles();
}

/*文件拖拉*/
bool MainFrame::OnDropFiles(wxCoord /*x*/, wxCoord /*y*/, const wxArrayString &files) {
    /*安全实例初始化?*/
    bool success = true;


    /*首先检查是否传递了工作区.如果是的话,只会加载这个*/
    wxString foundWorkspace;
    for (unsigned int i = 0; i < files.GetCount(); ++i) {
        FileType ft = FileTypeOf(files[i]);
        if (ft == ftCodeBlocksWorkspace || ft == ftMSVC6Workspace || ft == ftMSVC7Workspace) {
            foundWorkspace = files[i];
            break;
        }
    }

    if (!foundWorkspace.IsEmpty())
        success &= OpenGeneric(foundWorkspace);
    else {
        wxBusyCursor useless;
        /*
         * wxWidgets3.1.2格式
         * wxPaintEvent e;
         * ProcessEvent(e);
         * */
        //wxPaintEvent e(this);
        //ProcessEvent(e);
        for (unsigned int i = 0; i < files.GetCount(); ++i)
            success &= OpenGeneric(files[i]);
    }
    return success;
}

void MainFrame::OnFileNew(cb_unused wxCommandEvent &event) {
    wxMenu *popup = nullptr;
    wxMenuBar *bar = GetMenuBar();
    if (!bar) { return; }

    bar->FindItem(idFileNewProject, &popup);
    if (popup) {
        popup = CopyMenu(popup);
        if (popup) {
            /*这将引导我们进入OnFileNewWhat()-肉在那里:)*/
            PopupMenu(popup);
            delete popup;
        }
    }
}

/*
如果我们要打开一个项目(bProject==true),我们不想干扰"最后一种类型的文件"(可能调用是在"此处开始"页上打开(现有)的项目
 -->因此我们知道它是一个项目
 -->相应地设置筛选器,但如上所述,不要强制更改"最后使用的文件类型",
只有在执行打开的文件(so(source)file<-->project(file))
TODO:当常规文件打开并且用户手动将筛选器设置为项目文件时,才会更改最后一种类型:这是预期的行为吗???
*/

/*打开文件*/
void MainFrame::DoOnFileOpen(bool bProject) {
    wxString Filters = FileFilters::GetFilterString();

    /*GetIndexForFilterAll()返回的值由GetFilterString()更新*/
    int StoredIndex = FileFilters::GetIndexForFilterAll();
    wxString Path;
    ConfigManager *mgr = Manager::Get()->GetConfigManager(_T("app"));
    if (mgr) {
        if (!bProject) {
            wxString Filter = mgr->Read(_T("/file_dialogs/file_new_open/filter"));
            if (!Filter.IsEmpty())
                FileFilters::GetFilterIndexFromName(Filters, Filter, StoredIndex);
            Path = mgr->Read(_T("/file_dialogs/file_new_open/directory"), Path);
        } else
            FileFilters::GetFilterIndexFromName(Filters, wxT("Code::Blocks项目/工作区文件"), StoredIndex);
    }
    wxFileDialog dlg(this,
                     wxT("打开文件"),
                     Path,
                     wxEmptyString,
                     Filters,
                     wxFD_OPEN | wxFD_MULTIPLE | compatibility::wxHideReadonly);
    dlg.SetFilterIndex(StoredIndex);

    PlaceWindow(&dlg);
    if (dlg.ShowModal() == wxID_OK) {
        /*如上所述存储上次使用的筛选器和目录:在"打开项目"的情况下不要执行此操作*/
        if (mgr && !bProject) {
            int Index = dlg.GetFilterIndex();
            wxString Filter;
            if (FileFilters::GetFilterNameFromIndex(Filters, Index, Filter))
                mgr->Write(_T("/file_dialogs/file_new_open/filter"), Filter);
            wxString Test = dlg.GetDirectory();
            mgr->Write(_T("/file_dialogs/file_new_open/directory"), dlg.GetDirectory());
        }
        wxArrayString files;
        dlg.GetPaths(files);
        OnDropFiles(0, 0, files);
    }
}

void MainFrame::OnFileOpen(cb_unused wxCommandEvent &event) {
    /*通过"文件"菜单(不确定是否要打开项目)*/
    DoOnFileOpen(false);
}

void MainFrame::OnFileReopenProject(wxCommandEvent &event) {
    size_t id = event.GetId() - wxID_CBFILE17;
    wxString fname = m_projectsHistory.GetHistoryFile(id);
    if (!OpenGeneric(fname, true))
        m_projectsHistory.AskToRemoveFileFromHistory(id);
}

void MainFrame::OnFileOpenRecentProjectClearHistory(cb_unused wxCommandEvent &event) {
    m_projectsHistory.ClearHistory();
}

void MainFrame::OnFileReopen(wxCommandEvent &event) {
    size_t id = event.GetId() - wxID_CBFILE01;
    wxString fname = m_filesHistory.GetHistoryFile(id);
    if (!OpenGeneric(fname, true))
        m_filesHistory.AskToRemoveFileFromHistory(id);
}

void MainFrame::OnFileOpenRecentClearHistory(cb_unused wxCommandEvent &event) {
    m_filesHistory.ClearHistory();
}

void MainFrame::OnFileSave(cb_unused wxCommandEvent &event) {
    if (!Manager::Get()->GetEditorManager()->SaveActive()) {
        wxString msg;
        msg.Printf(wxT("无法保存文件%s..."), Manager::Get()->GetEditorManager()->GetActiveEditor()->GetFilename().wx_str());
        cbMessageBox(msg, wxT("保存文件时出错"), wxICON_ERROR);
    }
    DoUpdateStatusBar();
}

void MainFrame::OnFileSaveAs(cb_unused wxCommandEvent &event) {
    Manager::Get()->GetEditorManager()->SaveActiveAs();
    DoUpdateStatusBar();
}

void MainFrame::OnFileSaveProject(cb_unused wxCommandEvent &event) {
    /*不需要调用SaveActiveProjectAs(),因为这是在cbProject::Save()中处理的*/
    ProjectManager *prjManager = Manager::Get()->GetProjectManager();
    if (prjManager->SaveActiveProject())
        m_projectsHistory.AddToHistory(prjManager->GetActiveProject()->GetFilename());
    DoUpdateStatusBar();
    DoUpdateAppTitle();
}

void MainFrame::OnFileSaveProjectAs(cb_unused wxCommandEvent &event) {
    ProjectManager *prjManager = Manager::Get()->GetProjectManager();
    if (prjManager->SaveActiveProjectAs())
        m_projectsHistory.AddToHistory(prjManager->GetActiveProject()->GetFilename());
    DoUpdateStatusBar();
    DoUpdateAppTitle();
}

void MainFrame::OnFileSaveAll(cb_unused wxCommandEvent &event) {
    Manager::Get()->GetConfigManager(_T("app"))->Flush();
    Manager::Get()->GetEditorManager()->SaveAll();
    ProjectManager *prjManager = Manager::Get()->GetProjectManager();
    prjManager->SaveAllProjects();

    if (prjManager->GetWorkspace()->GetModified()
        && !prjManager->GetWorkspace()->IsDefault()
        && prjManager->SaveWorkspace()) {
        m_projectsHistory.AddToHistory(prjManager->GetWorkspace()->GetFilename());
    }
    DoUpdateStatusBar();
    DoUpdateAppTitle();
}

void MainFrame::OnFileSaveProjectTemplate(cb_unused wxCommandEvent &event) {
    TemplateManager::Get()->SaveUserTemplate(Manager::Get()->GetProjectManager()->GetActiveProject());
}

void MainFrame::OnFileCloseProject(cb_unused wxCommandEvent &event) {
    /*我们实际上并没有在这里关闭,但我们想检查活动项目是否仍在打开文件(仍很忙)*/
    if (!ProjectManager::CanShutdown() || !EditorManager::CanShutdown()) {
        wxBell();
        return;
    }
    Manager::Get()->GetProjectManager()->CloseActiveProject();
    DoUpdateStatusBar();
}

void MainFrame::OnFileImportProjectDevCpp(cb_unused wxCommandEvent &event) {
    OpenGeneric(ShowOpenFileDialog(wxT("导入Dev-C++项目"), FileFilters::GetFilterString(_T('.') + FileFilters::DEVCPP_EXT)), false);
}

void MainFrame::OnFileImportProjectMSVC(cb_unused wxCommandEvent &event) {
    OpenGeneric(ShowOpenFileDialog(wxT("导入MS Visual C++ 6.0项目"), FileFilters::GetFilterString(_T('.') + FileFilters::MSVC6_EXT)), false);
}

void MainFrame::OnFileImportProjectMSVCWksp(cb_unused wxCommandEvent &event) {
    OpenGeneric(ShowOpenFileDialog(wxT("导入MS Visual C++ 6.0工作区"), FileFilters::GetFilterString(_T('.') + FileFilters::MSVC6_WORKSPACE_EXT)), false);
}

void MainFrame::OnFileImportProjectMSVS(cb_unused wxCommandEvent &event) {
    OpenGeneric(ShowOpenFileDialog(wxT("导入MS Visual Studio 7.0+ 项目"), FileFilters::GetFilterString(_T('.') + FileFilters::MSVC7_EXT)), false);
}

void MainFrame::OnFileImportProjectMSVSWksp(cb_unused wxCommandEvent &event) {
    OpenGeneric(ShowOpenFileDialog(wxT("导入MS Visual Studio 7.0+解决方案"), FileFilters::GetFilterString(_T('.') + FileFilters::MSVC7_WORKSPACE_EXT)), false);
}

void MainFrame::OnFileOpenDefWorkspace(cb_unused wxCommandEvent &event) {
    ProjectManager *pman = Manager::Get()->GetProjectManager();
    if (!pman->GetWorkspace()->IsDefault() && !pman->LoadWorkspace()) {
        /*不要在"最近的项目"列表中添加默认工作区它总是单击一个菜单*/
        cbMessageBox(wxT("无法打开默认工作区(文件是否存在?)"), wxT("警告"), wxICON_WARNING);
    }
}

void MainFrame::OnFileSaveWorkspace(cb_unused wxCommandEvent &event) {
    ProjectManager *pman = Manager::Get()->GetProjectManager();
    if (pman->SaveWorkspace())
        m_projectsHistory.AddToHistory(pman->GetWorkspace()->GetFilename());
}

void MainFrame::OnFileSaveWorkspaceAs(cb_unused wxCommandEvent &event) {
    ProjectManager *pman = Manager::Get()->GetProjectManager();
    if (pman->SaveWorkspaceAs(_T("")))
        m_projectsHistory.AddToHistory(pman->GetWorkspace()->GetFilename());
}

void MainFrame::OnFileCloseWorkspace(cb_unused wxCommandEvent &event) {
    DoCloseCurrentWorkspace();
}

void MainFrame::OnFileClose(cb_unused wxCommandEvent &event) {
    Manager::Get()->GetEditorManager()->CloseActive();
    DoUpdateStatusBar();
    Refresh();
}

void MainFrame::OnFileCloseAll(cb_unused wxCommandEvent &event) {
    Manager::Get()->GetEditorManager()->CloseAll();
    DoUpdateStatusBar();
}

void MainFrame::OnFileNext(cb_unused wxCommandEvent &event) {
    Manager::Get()->GetEditorManager()->ActivateNext();
    DoUpdateStatusBar();
}

void MainFrame::OnFilePrev(cb_unused wxCommandEvent &event) {
    Manager::Get()->GetEditorManager()->ActivatePrevious();
    DoUpdateStatusBar();
}

void MainFrame::OnFilePrint(cb_unused wxCommandEvent &event) {
    PrintDialog dlg(this);
    PlaceWindow(&dlg);
    if (dlg.ShowModal() == wxID_OK)
        Manager::Get()->GetEditorManager()->Print(dlg.GetPrintScope(), dlg.GetPrintColourMode(), dlg.GetPrintLineNumbers());
}

void MainFrame::OnFileQuit(cb_unused wxCommandEvent &event) {
    Close(false);
}

void MainFrame::OnEraseBackground(wxEraseEvent &event) {
    /*无闪烁显示*/
    event.Skip();
}

void MainFrame::OnSize(wxSizeEvent &event) {
    if (m_pHighlightButton) {
        wxRect rect;
        if (GetStatusBar()->GetFieldRect(1, rect)) {
            m_pHighlightButton->SetPosition(rect.GetPosition());
            m_pHighlightButton->SetSize(rect.GetSize());
        }
    }
    event.Skip();
}

void MainFrame::OnApplicationClose(wxCloseEvent &event) {
    if (m_InitiatedShutdown){return;}

    CodeBlocksEvent evt(cbEVT_APP_START_SHUTDOWN);
    Manager::Get()->ProcessEvent(evt);
    Manager::Yield();

    m_InitiatedShutdown = true;
    Manager::BlockYields(true);

    {
        /*检查是否有编译器插件正在生成,并询问用户是否要停止它.*/
        bool hasRunning = cbHasRunningCompilers(Manager::Get()->GetPluginManager());
        if (hasRunning) {
            int result = cbMessageBox(wxT("正在编译,停止编译并退出?"),wxT("问题"), wxYES_NO | wxNO_DEFAULT | wxICON_QUESTION,this);
            if (result == wxID_YES)
                cbStopRunningCompilers(Manager::Get()->GetPluginManager());
            else {
                event.Veto();
                wxBell();
                m_InitiatedShutdown = false;
                Manager::BlockYields(false);
                return;
            }
        }
    }

    if (!ProjectManager::CanShutdown() || !EditorManager::CanShutdown()) {
        event.Veto();
        wxBell();
        m_InitiatedShutdown = false;
        Manager::BlockYields(false);
        return;
    }

    if (!DoCloseCurrentWorkspace()) {
        event.Veto();
        m_InitiatedShutdown = false;
        Manager::BlockYields(false);
        return;
    }
    Manager::SetAppShuttingDown(true);
    Manager::Get()->GetLogManager()->DebugLog(wxT("正在取消初始化插件..."));
    CodeBlocksEvent evtShutdown(cbEVT_APP_START_SHUTDOWN);
    Manager::Get()->ProcessEvent(evtShutdown);
    Manager::Yield();

    if (!Manager::IsBatchBuild())
        SaveWindowState();

    if (m_pPrjManUI->GetNotebook())
        m_LayoutManager.DetachPane(m_pPrjManUI->GetNotebook());
    m_LayoutManager.DetachPane(m_pInfoPane);
    m_LayoutManager.DetachPane(Manager::Get()->GetEditorManager()->GetNotebook());

    m_LayoutManager.UnInit();
    TerminateRecentFilesHistory();

    /*从这个窗口中删除所有其他事件处理程序,这样在没有加载插件时,它就不会崩溃*/
    while (GetEventHandler() != this){PopEventHandler(false);}

    /*隐藏窗口*/
    Hide();

    if (!Manager::IsBatchBuild()) {
        m_pInfoPane->Destroy();
        m_pInfoPane = nullptr;
    }

    /*断开工具栏的鼠标右键单击事件处理程序的连接,这应该在在Manager::Shutdown()中卸载插件之前完成.*/
    PluginToolbarsMap::iterator it;
    for (it = m_PluginsTools.begin(); it != m_PluginsTools.end(); ++it) {
        wxToolBar *toolbar = it->second;
        /*在销毁工具栏之前断开鼠标右键单击事件处理程序的连接*/
        if (toolbar)
        {
            bool result = toolbar->Disconnect(wxID_ANY, wxEVT_COMMAND_TOOL_RCLICKED, wxCommandEventHandler(MainFrame::OnToolBarRightClick));
            cbAssert(result);
        }
    }

    /*Shutdown()不是Free(),管理器在退出时自动销毁*/
    Manager::Shutdown();
    Destroy();
}

void MainFrame::OnEditSwapHeaderSource(cb_unused wxCommandEvent &event) {
    Manager::Get()->GetEditorManager()->SwapActiveHeaderSource();
    DoUpdateStatusBar();
}

void MainFrame::OnEditGotoMatchingBrace(cb_unused wxCommandEvent &event) {
    cbEditor *ed = Manager::Get()->GetEditorManager()->GetBuiltinActiveEditor();
    if (ed)
        ed->GotoMatchingBrace();
}

void MainFrame::OnEditBookmarksToggle(cb_unused wxCommandEvent &event) {
    EditorBase *ed = Manager::Get()->GetEditorManager()->GetActiveEditor();
    if (ed && ed->IsBuiltinEditor())
        static_cast<cbEditor *>(ed)->ToggleBookmark();
}

void MainFrame::OnEditBookmarksNext(cb_unused wxCommandEvent &event) {
    EditorBase *ed = Manager::Get()->GetEditorManager()->GetActiveEditor();
    if (ed && ed->IsBuiltinEditor())
        static_cast<cbEditor *>(ed)->GotoNextBookmark();
}

void MainFrame::OnEditBookmarksPrevious(cb_unused wxCommandEvent &event) {
    EditorBase *ed = Manager::Get()->GetEditorManager()->GetActiveEditor();
    if (ed && ed->IsBuiltinEditor())
        static_cast<cbEditor *>(ed)->GotoPreviousBookmark();
}

void MainFrame::OnEditBookmarksClearAll(wxCommandEvent &event) {
    EditorBase *ed = Manager::Get()->GetEditorManager()->GetActiveEditor();
    if (ed && ed->IsBuiltinEditor())
        static_cast<cbEditor *>(ed)->ClearAllBookmarks();
}

void MainFrame::OnEditUndo(cb_unused wxCommandEvent &event) {
    EditorBase *ed = Manager::Get()->GetEditorManager()->GetActiveEditor();
    if (ed && ed->CanUndo()) {
        cbEditor *cbEd = Manager::Get()->GetEditorManager()->GetBuiltinEditor(ed);
        if (cbEd && cbEd->GetControl()->AutoCompActive())
            cbEd->GetControl()->AutoCompCancel();
        ed->Undo();
    }
}

void MainFrame::OnEditRedo(cb_unused wxCommandEvent &event) {
    EditorBase *ed = Manager::Get()->GetEditorManager()->GetActiveEditor();
    if (ed)
        ed->Redo();
}

void MainFrame::OnEditClearHistory(cb_unused wxCommandEvent &event) {
    EditorBase *ed = Manager::Get()->GetEditorManager()->GetActiveEditor();
    if (ed)
        ed->ClearHistory();
}

void MainFrame::OnEditCopy(cb_unused wxCommandEvent &event) {
    EditorBase *ed = Manager::Get()->GetEditorManager()->GetActiveEditor();
    if (ed)
        ed->Copy();
}

void MainFrame::OnEditCut(cb_unused wxCommandEvent &event) {
    EditorBase *ed = Manager::Get()->GetEditorManager()->GetActiveEditor();
    if (ed)
        ed->Cut();
}

void MainFrame::OnEditPaste(cb_unused wxCommandEvent &event) {
    EditorBase *ed = Manager::Get()->GetEditorManager()->GetActiveEditor();
    if (ed)
        ed->Paste();
}

void MainFrame::OnEditParaUp(cb_unused wxCommandEvent &event) {
    cbEditor *ed = Manager::Get()->GetEditorManager()->GetBuiltinActiveEditor();
    if (ed)
        ed->GetControl()->ParaUp();
}

void MainFrame::OnEditParaUpExtend(cb_unused wxCommandEvent &event) {
    cbEditor *ed = Manager::Get()->GetEditorManager()->GetBuiltinActiveEditor();
    if (ed)
        ed->GetControl()->ParaUpExtend();
}

void MainFrame::OnEditParaDown(cb_unused wxCommandEvent &event) {
    cbEditor *ed = Manager::Get()->GetEditorManager()->GetBuiltinActiveEditor();
    if (ed)
        ed->GetControl()->ParaDown();
}

void MainFrame::OnEditParaDownExtend(cb_unused wxCommandEvent &event) {
    cbEditor *ed = Manager::Get()->GetEditorManager()->GetBuiltinActiveEditor();
    if (ed)
        ed->GetControl()->ParaDownExtend();
}

void MainFrame::OnEditWordPartLeft(cb_unused wxCommandEvent &event) {
    cbEditor *ed = Manager::Get()->GetEditorManager()->GetBuiltinActiveEditor();
    if (ed)
        ed->GetControl()->WordPartLeft();
}

void MainFrame::OnEditWordPartLeftExtend(cb_unused wxCommandEvent &event) {
    cbEditor *ed = Manager::Get()->GetEditorManager()->GetBuiltinActiveEditor();
    if (ed)
        ed->GetControl()->WordPartLeftExtend();
}

void MainFrame::OnEditWordPartRight(cb_unused wxCommandEvent &event) {
    cbEditor *ed = Manager::Get()->GetEditorManager()->GetBuiltinActiveEditor();
    if (ed)
        ed->GetControl()->WordPartRight();
}

void MainFrame::OnEditWordPartRightExtend(cb_unused wxCommandEvent &event) {
    cbEditor *ed = Manager::Get()->GetEditorManager()->GetBuiltinActiveEditor();
    if (ed)
        ed->GetControl()->WordPartRightExtend();
}

void MainFrame::OnEditZoomIn(cb_unused wxCommandEvent &event) {
    cbEditor *ed = Manager::Get()->GetEditorManager()->GetBuiltinActiveEditor();
    if (ed)
        ed->GetControl()->ZoomIn();
}

void MainFrame::OnEditZoomOut(cb_unused wxCommandEvent &event) {
    cbEditor *ed = Manager::Get()->GetEditorManager()->GetBuiltinActiveEditor();
    if (ed)
        ed->GetControl()->ZoomOut();
}

void MainFrame::OnEditZoomReset(cb_unused wxCommandEvent &event) {
    cbEditor *ed = Manager::Get()->GetEditorManager()->GetBuiltinActiveEditor();
    if (ed)
        ed->GetControl()->SetZoom(0);
}

void MainFrame::OnEditLineCut(cb_unused wxCommandEvent &event) {
    cbEditor *ed = Manager::Get()->GetEditorManager()->GetBuiltinActiveEditor();
    if (ed)
        ed->GetControl()->LineCut();
}

void MainFrame::OnEditLineDelete(cb_unused wxCommandEvent &event) {
    cbEditor *ed = Manager::Get()->GetEditorManager()->GetBuiltinActiveEditor();
    if (ed)
        ed->GetControl()->LineDelete();
}

void MainFrame::OnEditLineDuplicate(cb_unused wxCommandEvent &event) {
    cbEditor *ed = Manager::Get()->GetEditorManager()->GetBuiltinActiveEditor();
    if (ed)
        ed->GetControl()->LineDuplicate();
}

void MainFrame::OnEditLineTranspose(cb_unused wxCommandEvent &event) {
    cbEditor *ed = Manager::Get()->GetEditorManager()->GetBuiltinActiveEditor();
    if (ed)
        ed->GetControl()->LineTranspose();
}

void MainFrame::OnEditLineCopy(cb_unused wxCommandEvent &event) {
    cbEditor *ed = Manager::Get()->GetEditorManager()->GetBuiltinActiveEditor();
    if (ed)
        ed->GetControl()->LineCopy();
}

/*行粘帖*/
void MainFrame::OnEditLinePaste(cb_unused wxCommandEvent &event) {
    cbEditor *ed = Manager::Get()->GetEditorManager()->GetBuiltinActiveEditor();
    if (!ed) { return; }
    cbStyledTextCtrl *edc=ed->GetControl();
    /*我们想要在这个步骤撤销所有动作*/
    if (edc) {
        edc->BeginUndoAction();
        int pos = edc->GetCurrentPos();
        int line = edc->LineFromPosition(pos);
        edc->GotoLine(line);
        int column = pos - edc->GetCurrentPos();
        edc->Paste();
        pos = edc->GetCurrentPos();
        edc->GotoPos(pos + column);
        edc->EndUndoAction();
    }
}

void MainFrame::OnEditLineMove(wxCommandEvent &event) {
    cbEditor *ed = Manager::Get()->GetEditorManager()->GetBuiltinActiveEditor();
    if (!ed) { return; }

    /*TODO(mortenmacfly):是否在此处排除矩形选择?大多数用户在这里期望什么样的行为???*/
    cbStyledTextCtrl *stc = ed->GetControl();
    if (!stc || /*stc->SelectionIsRectangle() ||*/ (stc->GetSelections() > 1)){return;}

    /*你为什么要试图移动行?*/
    if (stc->GetLineCount() < 3) {return;}

    /*注意,这些函数调用要简单得多,但是它们会导致不必要地将文本标记为已修改*/
    /*if (event.GetId() == idEditLineUp) { stc->MoveSelectedLinesUp(); } else { stc->MoveSelectedLinesDown(); }*/

    int startPos = stc->PositionFromLine(stc->LineFromPosition(stc->GetSelectionStart()));
    /*是行*/
    int endPos = stc->LineFromPosition(stc->GetSelectionEnd());
    /*结束在第一列/这行有文本/起点和终点在不同的线上*/
    if (stc->GetSelectionEnd() == stc->PositionFromLine(endPos) && stc->PositionFromLine(endPos) != stc->GetLineEndPosition(endPos) && endPos > stc->LineFromPosition(startPos))
    {
        /*不要意外地选择另一行*/
        --endPos;
    }
    const bool isLastLine = (endPos == stc->GetLineCount() - 1);

    /*警告:stc->GetLineEndPosition(endPos)为CR LF生成奇怪的结果(参见错误#18892#)*/
    /*是最后一行否则是位置*/
    endPos = (isLastLine ? stc->GetLineEndPosition(endPos): stc->PositionFromLine(endPos + 1) - 1);
    if (event.GetId() == idEditLineUp) {
        /*无法向上移动(我们在顶部),退出*/
        if (stc->LineFromPosition(startPos) < 1) { return; }

        stc->BeginUndoAction();
        const int offset = (isLastLine ? startPos - stc->GetLineEndPosition(stc->LineFromPosition(startPos) - 1) : 0);
        const int lineLength = startPos - stc->PositionFromLine(stc->LineFromPosition(startPos) - 1);
        const wxString line = stc->GetTextRange(startPos - lineLength - offset,startPos - offset);

        stc->InsertText(endPos + (isLastLine ? 0 : 1), line);
        /*警告:line.Length()!=lineLength 如果使用多字节字符*/
        stc->DeleteRange(startPos - lineLength, lineLength);
        startPos -= lineLength;
        endPos -= lineLength;
        stc->EndUndoAction();
    } else{
        /*event.GetId() == idEditLineDown 不能向下移动(我们在底部),退出*/
        if (isLastLine){return;}
        stc->BeginUndoAction();
        const int lineLength = stc->PositionFromLine(stc->LineFromPosition(endPos + 1) + 1) - endPos - 1;
        const wxString line = stc->GetTextRange(endPos + 1,
                                                endPos + 1 + lineLength);
        stc->InsertText(startPos, line);
        /*警告: line.Length() != lineLength 如果使用多字节字符*/
        startPos += lineLength;
        endPos += lineLength;
        stc->DeleteRange(endPos + 1, lineLength);
        stc->EndUndoAction();
    }
    stc->SetSelectionVoid(startPos, endPos);
}

void MainFrame::OnEditUpperCase(cb_unused wxCommandEvent &event) {
    cbEditor *ed = Manager::Get()->GetEditorManager()->GetBuiltinActiveEditor();
    if (ed)
        ed->GetControl()->UpperCase();
}

void MainFrame::OnEditLowerCase(cb_unused wxCommandEvent &event) {
    cbEditor *ed = Manager::Get()->GetEditorManager()->GetBuiltinActiveEditor();
    if (ed)
        ed->GetControl()->LowerCase();
}

void MainFrame::OnEditInsertNewLine(wxCommandEvent &event) {
    OnEditGotoLineEnd(event);
    cbEditor *ed = Manager::Get()->GetEditorManager()->GetBuiltinActiveEditor();
    if (ed) {
        cbStyledTextCtrl *stc = ed->GetControl();
        if (stc->AutoCompActive())
            stc->AutoCompCancel();
        stc->NewLine();
    }
}

void MainFrame::OnEditGotoLineEnd(cb_unused wxCommandEvent &event) {
    cbEditor *ed = Manager::Get()->GetEditorManager()->GetBuiltinActiveEditor();
    if (ed) {
        cbStyledTextCtrl *stc = ed->GetControl();
        if (stc->AutoCompActive())
            stc->AutoCompCancel();
        stc->LineEnd();
    }
}

static void InsertNewLine(bool below) {
    cbEditor *ed = Manager::Get()->GetEditorManager()->GetBuiltinActiveEditor();
    if (ed) {
        cbStyledTextCtrl *stc = ed->GetControl();
        stc->BeginUndoAction();
        if (stc->AutoCompActive())
            stc->AutoCompCancel();

        if (below) {
            stc->LineEndDisplay();
            int pos = stc->GetCurrentPos();
            stc->InsertText(pos, GetEOLStr(stc->GetEOLMode()));
            stc->LineDown();
        } else {
            stc->HomeDisplay();
            int pos = stc->GetCurrentPos();
            stc->InsertText(pos, GetEOLStr(stc->GetEOLMode()));
            stc->EndUndoAction();
        }
        stc->EndUndoAction();
    }
}

void MainFrame::OnEditInsertNewLineBelow(wxCommandEvent &event) {
    InsertNewLine(true);
}

void MainFrame::OnEditInsertNewLineAbove(wxCommandEvent &event) {
    InsertNewLine(false);
}

void MainFrame::OnEditSelectAll(cb_unused wxCommandEvent &event) {
    EditorBase *eb = Manager::Get()->GetEditorManager()->GetActiveEditor();
    if (eb)
        eb->SelectAll();
}

namespace {
    /*编辑器选定内容结构*/
    struct EditorSelection {
        long caret, anchor;
        bool Empty() const { return caret == anchor; }
        bool IsReversed() const { return caret < anchor; }
        long GetStart() const { return std::min(caret, anchor); }
        long GetEnd() const { return std::max(caret, anchor); }
        bool Contains(const EditorSelection &selection) const {
            return !(GetEnd() < selection.GetStart() || GetStart() > selection.GetEnd());
        }
    };

    bool SelectNext(cbStyledTextCtrl *control, const wxString &selectedText, long selectionEnd, bool reversed) {

        /*如果没有特殊字符,请始终匹配大小写并尝试匹配整个单词*/
        int flag = wxSCI_FIND_MATCHCASE;
        if (selectedText.find_first_of(wxT(";:\"'`~@#$%^,-+*/\\=|!?&*(){}[]")) == wxString::npos) { flag |= wxSCI_FIND_WHOLEWORD; }

        /*我们需要这个来正确处理多字节字符*/
        int lengthFound = 0;
        int eof = control->GetLength();
        int pos = control->FindText(selectionEnd, eof, selectedText, flag, &lengthFound);
        if (pos != wxSCI_INVALID_POSITION) {
            control->SetAdditionalSelectionTyping(true);
            control->IndicatorClearRange(pos, lengthFound);
            if (reversed)
                control->AddSelection(pos, pos + lengthFound);
            else
                control->AddSelection(pos + lengthFound, pos);
            control->MakeNearbyLinesVisible(control->LineFromPosition(pos));
            return true;
        } else {
            InfoWindow::Display(wxT("选择下一个事件"), wxT("没有更多可用的"));
            return false;
        }
    }

    bool GetSelectionInEditor(EditorSelection &selection, cbStyledTextCtrl *control) {
        int main = control->GetMainSelection();
        int count = control->GetSelections();
        if (main >= 0 && main < count) {
            selection.caret = control->GetSelectionNCaret(main);
            selection.anchor = control->GetSelectionNAnchor(main);
            return true;
        } else
            return false;
    }

} // anonymous namespace

void MainFrame::OnEditSelectNext(cb_unused wxCommandEvent &event) {
    EditorBase *eb = Manager::Get()->GetEditorManager()->GetActiveEditor();
    if (!eb || !eb->IsBuiltinEditor()){return;}
    cbStyledTextCtrl *control = static_cast<cbEditor *>(eb)->GetControl();

    EditorSelection selection;
    if (!GetSelectionInEditor(selection, control)) { return; }

    if (!selection.Empty()) {
        const wxString &selectedText(control->GetTextRange(selection.GetStart(), selection.GetEnd()));
        SelectNext(control, selectedText, selection.GetEnd(), selection.IsReversed());
    } else {
        /*如果未选择任何内容,请在光标位置选择单词.*/
        const int start = control->WordStartPosition(selection.caret, true);
        const int end = control->WordEndPosition(selection.caret, true);
        control->SetSelection(start, end);
    }
}

void MainFrame::OnEditSelectNextSkip(cb_unused wxCommandEvent &event) {
    EditorBase *eb = Manager::Get()->GetEditorManager()->GetActiveEditor();
    if (!eb || !eb->IsBuiltinEditor()){return;}
    cbStyledTextCtrl *control = static_cast<cbEditor *>(eb)->GetControl();

    EditorSelection selection;
    if (!GetSelectionInEditor(selection, control)) { return; }

    ConfigManager *cfgEditor = Manager::Get()->GetConfigManager(wxT("editor"));
    bool highlightOccurrences = cfgEditor->ReadBool(wxT("/highlight_occurrence/enabled"), true);

    /*
     首先选择下一个匹配项.
     这可以防止在只有一个选择时用户使用命令时在文件开头创建光标.
     闪烁始终确保至少有一个选择/光标.
     因此,如果清除所有选择,它会在文件的开头创建一个光标.
     */
    const wxString &selectedText(control->GetTextRange(selection.GetStart(), selection.GetEnd()));
    /*如果没有新的选择,不要取消选择当前的选择.*/
    if (!SelectNext(control, selectedText, selection.GetEnd(), selection.IsReversed())) { return; }
    /*将所选内容存储在矢量容器中,但当前矢量容器除外*/
    typedef std::vector<EditorSelection> Selections;
    Selections selections;
    int count = control->GetSelections();
    for (int ii = 0; ii < count; ++ii) {
        EditorSelection item;
        item.caret = control->GetSelectionNCaret(ii);
        item.anchor = control->GetSelectionNAnchor(ii);

        if (!item.Contains(selection)){
            selections.push_back(item);
        }else if (highlightOccurrences) {
            /*如果已启用,则还原突出显示事件的指示器.*/
            control->IndicatorFillRange(item.GetStart(), item.GetEnd());
        }
    }

    control->ClearSelections();
    Selections::const_iterator it = selections.begin();
    int index = 0;
    if (it != selections.end() && control->GetSelections() > 0) {
        control->SetSelectionNAnchor(index, it->anchor);
        control->SetSelectionNCaret(index, it->caret);
        ++index;
        ++it;
    }
    for (; it != selections.end(); ++it) {
        control->AddSelection(it->caret, it->anchor);
        ++index;
    }
}

/*这是对原来的OnEditCommentSelected函数的无耻剽窃,现在更适合命名为OnEditToggleCommentSelected(因为那是它的作用:)*/
void MainFrame::OnEditCommentSelected(cb_unused wxCommandEvent &event) {
    cbEditor *ed = Manager::Get()->GetEditorManager()->GetBuiltinActiveEditor();
    if (!ed) { return; }

    cbStyledTextCtrl *stc = ed->GetControl();
    if (!stc) { return; }
    EditorColourSet *colour_set = Manager::Get()->GetEditorManager()->GetColourSet();
    if (!colour_set) { return; }

    CommentToken comment = colour_set->GetCommentToken(ed->GetLanguage());
    if (comment.lineComment == wxEmptyString && comment.streamCommentStart == wxEmptyString) { return; }

    stc->BeginUndoAction();
    if (wxSCI_INVALID_POSITION != stc->GetSelectionStart()) {
        int startLine = stc->LineFromPosition(stc->GetSelectionStart());
        int endLine = stc->LineFromPosition(stc->GetSelectionEnd());
        int curLine = startLine;
        /*修正一个小问题:当选择多行并且插入符号在最后一行之后的行的开头时,代码会错误地(取消)注释该行.这就修正了它.*/
        /*所选内容不止一行而且插入符号位于行首*/
        if (startLine != endLine && stc->GetColumn(stc->GetSelectionEnd()) == 0) {
            /*不要考虑插入符号所在的行,因为它不包含任何选择(caret_column==0)...*/
            --endLine;
        }

        while (curLine <= endLine) {
            /*每行:注释.*/
            if (comment.lineComment != wxEmptyString) { stc->InsertText(stc->PositionFromLine(curLine), comment.lineComment); }
            else {
                /*如果语言不支持行注释,请使用流注释*/
                stc->InsertText(stc->PositionFromLine(curLine), comment.streamCommentStart);
                stc->InsertText(stc->GetLineEndPosition(curLine), comment.streamCommentEnd);
            }
            ++curLine;
        }
        stc->SetSelectionVoid(stc->PositionFromLine(startLine), stc->PositionFromLine(endLine) + stc->LineLength(endLine));
    }
    stc->EndUndoAction();
}

/*有关详细信息,请参见上文(OnEditCommentSelected).*/
void MainFrame::OnEditUncommentSelected(cb_unused wxCommandEvent &event) {
    cbEditor *ed = Manager::Get()->GetEditorManager()->GetBuiltinActiveEditor();
    if (!ed) { return; }
    cbStyledTextCtrl *stc = ed->GetControl();
    if (!stc) { return; }
    EditorColourSet *colour_set = Manager::Get()->GetEditorManager()->GetColourSet();
    if (!colour_set) { return; }
    CommentToken comment = colour_set->GetCommentToken(ed->GetLanguage());
    if (comment.lineComment == wxEmptyString && comment.streamCommentStart == wxEmptyString) { return; }

    stc->BeginUndoAction();
    if (wxSCI_INVALID_POSITION != stc->GetSelectionStart()) {
        int startLine = stc->LineFromPosition(stc->GetSelectionStart());
        int endLine = stc->LineFromPosition(stc->GetSelectionEnd());
        int curLine = startLine;
        /*修正一个小问题:当选择多行并且插入符号在最后一行之后的行的开头时,代码会错误地(取消)注释该行.这就修正了它.*/
        /*所选内容不止一行而且插入符号位于行首*/
        if (startLine != endLine &&
            stc->GetColumn(stc->GetSelectionEnd()) == 0)
        {
            /*不要考虑插入符号所在的行,因为它不包含任何选择(caret_column==0)...*/
            --endLine;
        }

        while (curLine <= endLine) {
            /*对于每一行:如果已注释,则取消注释.*/
            wxString strLine = stc->GetLine(curLine);

            bool startsWithComment;
            bool endsWithComment;
            /*检查行注释*/
            startsWithComment = strLine.Strip(wxString::leading).StartsWith(comment.lineComment);
            if (startsWithComment) {
                /*我们知道有注释(可能前面有空格)*/
                int Pos = strLine.Find(comment.lineComment);
                int start = stc->PositionFromLine(curLine) + Pos;
                int end = start + comment.lineComment.Length();
                stc->SetTargetStart(start);
                stc->SetTargetEnd(end);
                stc->ReplaceTarget(wxEmptyString);
            }

            /*检查流注释*/
            /*检查流注释开始*/
            startsWithComment = strLine.Strip(wxString::leading).StartsWith(comment.streamCommentStart);
            /*检查流注释结束*/
            endsWithComment = strLine.Strip(wxString::trailing).EndsWith(comment.streamCommentEnd);
            if (startsWithComment && endsWithComment) {
                int Pos;
                int start;
                int end;

                /*我们知道有开始注释(可能前面有空格)*/
                Pos = strLine.Find(comment.streamCommentStart);
                start = stc->PositionFromLine(curLine) + Pos;
                end = start + comment.streamCommentStart.Length();
                stc->SetTargetStart(start);
                stc->SetTargetEnd(end);
                stc->ReplaceTarget(wxEmptyString);

                /*我们知道结尾处也有注释(后面可能还有空格) 注意!!我们必须减去已经删除的注释的长度*/
                Pos = strLine.rfind(comment.streamCommentEnd, strLine.npos) - comment.streamCommentStart.Length();
                start = stc->PositionFromLine(curLine) + Pos;
                end = start + comment.streamCommentEnd.Length();
                stc->SetTargetStart(start);
                stc->SetTargetEnd(end);
                stc->ReplaceTarget(wxEmptyString);
            }
            ++curLine;
        } // end while
        stc->SetSelectionVoid(stc->PositionFromLine(startLine), stc->PositionFromLine(endLine) + stc->LineLength(endLine));
    }
    stc->EndUndoAction();
}

void MainFrame::OnEditToggleCommentSelected(cb_unused wxCommandEvent &event) {
    cbEditor *ed = Manager::Get()->GetEditorManager()->GetBuiltinActiveEditor();
    if (!ed) { return; }

    cbStyledTextCtrl *stc = ed->GetControl();
    if (!stc) { return; }

    EditorColourSet *colour_set = Manager::Get()->GetEditorManager()->GetColourSet();
    if (!colour_set) { return; }

    wxString comment = colour_set->GetCommentToken(ed->GetLanguage()).lineComment;
    if (comment == wxEmptyString) { return; }

    stc->BeginUndoAction();
    if (wxSCI_INVALID_POSITION != stc->GetSelectionStart()) {
        int startLine = stc->LineFromPosition(stc->GetSelectionStart());
        int endLine = stc->LineFromPosition(stc->GetSelectionEnd());
        int curLine = startLine;
        /*修正一个小问题:当选择多行并且插入符号在最后一行之后的行的开头时,代码会错误地(取消)注释该行.这就修正了它.*/
        /*所选内容不止一行而且插入符号位于行首*/
        if (startLine != endLine && stc->GetColumn(stc->GetSelectionEnd()) == 0)
        {
            /*不要考虑插入符号所在的行,因为它不包含任何选择(caret_column==0)...*/
            --endLine;
        }

        bool doComment = false;
        while (curLine <= endLine) {
            /*检查是否对所选行中的任何行进行了注释*/
            wxString strLine = stc->GetLine(curLine);
            int commentPos = strLine.Strip(wxString::leading).Find(comment);

            if (commentPos != 0) {
                /*至少有一行未注释,因此请注释整个选定内容(否则,如果所有行都注释,请取消注释选定内容)*/
                doComment = true;
                break;
            }
            ++curLine;
        }

        curLine = startLine;
        while (curLine <= endLine) {
            if (doComment) { stc->InsertText(stc->PositionFromLine(curLine), comment); }
            else {
                /*我们知道有注释(可能前面有空格)*/
                wxString strLine = stc->GetLine(curLine);
                int Pos = strLine.Find(comment);
                int start = stc->PositionFromLine(curLine) + Pos;
                int end = start + comment.Length();
                stc->SetTargetStart(start);
                stc->SetTargetEnd(end);
                stc->ReplaceTarget(wxEmptyString);
            }
            ++curLine;
        }
        stc->SetSelectionVoid(stc->PositionFromLine(startLine), stc->PositionFromLine(endLine) + stc->LineLength(endLine));
    }
    stc->EndUndoAction();
}

void MainFrame::OnEditStreamCommentSelected(cb_unused wxCommandEvent &event) {
    cbEditor *ed = Manager::Get()->GetEditorManager()->GetBuiltinActiveEditor();
    if (!ed) { return; }

    cbStyledTextCtrl *stc = ed->GetControl();
    if (!stc) { return; }

    EditorColourSet *colour_set = Manager::Get()->GetEditorManager()->GetColourSet();
    if (!colour_set) { return; }

    CommentToken comment = colour_set->GetCommentToken(ed->GetLanguage());
    if (comment.streamCommentStart == wxEmptyString) { return; }

    stc->BeginUndoAction();
    if (wxSCI_INVALID_POSITION != stc->GetSelectionStart()) {
        int startPos = stc->GetSelectionStart();
        int endPos = stc->GetSelectionEnd();
        if (startPos == endPos) {
            /*如果没有选择流注释当前*单词*第一个*/
            startPos = stc->WordStartPosition(stc->GetCurrentPos(), true);
            endPos = stc->WordEndPosition(stc->GetCurrentPos(), true);
            if (startPos == endPos) {
                /*如果没有选择流注释当前*行**/
                startPos = stc->PositionFromLine(stc->LineFromPosition(startPos));
                endPos = stc->GetLineEndPosition(stc->LineFromPosition(startPos));
            }
        } else {
            /*修正一个小问题:当选择多行并且插入符号在最后一行之后的行的开头时,代码会错误地(取消)注释该行.这就修正了它.*/
            /*插入符号在行首*/
            if (stc->GetColumn(stc->GetSelectionEnd()) == 0) {
                /*不要考虑插入符号所在的行,因为它不包含任何选择(caret_column==0)...*/
                --endPos;
            }
        }

        /*流注释块*/
        int p1 = startPos - 1;
        while (stc->GetCharAt(p1) == _T(' ') && p1 > 0) { --p1; }
        p1 -= 1;
        int p2 = endPos;
        while (stc->GetCharAt(p2) == _T(' ') && p2 < stc->GetLength()) { ++p2; }
        const wxString start = stc->GetTextRange(p1, p1 + comment.streamCommentStart.Length());
        const wxString end = stc->GetTextRange(p2, p2 + comment.streamCommentEnd.Length());
        if (start == comment.streamCommentStart && end == comment.streamCommentEnd) {
            stc->SetTargetStart(p1);
            stc->SetTargetEnd(p2 + 2);
            wxString target = stc->GetTextRange(p1 + 2, p2);
            stc->ReplaceTarget(target);
            stc->GotoPos(p1 + target.Length());
        } else {
            stc->InsertText(startPos, comment.streamCommentStart);
            /*我们已经插入了一些字符,所以输出endPos改变了*/
            startPos += comment.streamCommentStart.Length();
            endPos += comment.streamCommentStart.Length();
            stc->InsertText(endPos, comment.streamCommentEnd);
            stc->SetSelectionVoid(startPos, endPos);
        }

    }
    stc->EndUndoAction();
}

void MainFrame::OnEditBoxCommentSelected(cb_unused wxCommandEvent &event) {
    cbEditor *ed = Manager::Get()->GetEditorManager()->GetBuiltinActiveEditor();
    if (!ed){return;}
    cbStyledTextCtrl *stc = ed->GetControl();
    if (!stc){return;}
    EditorColourSet *colour_set = Manager::Get()->GetEditorManager()->GetColourSet();
    if (!colour_set){return;}
    CommentToken comment = colour_set->GetCommentToken(ed->GetLanguage());
    if (comment.boxCommentStart == wxEmptyString){return;}

    stc->BeginUndoAction();
    if (wxSCI_INVALID_POSITION != stc->GetSelectionStart()) {
        int startLine = stc->LineFromPosition(stc->GetSelectionStart());
        int endLine = stc->LineFromPosition(stc->GetSelectionEnd());
        int curLine = startLine;
        /*修复一个小问题:当选择多行并且插入符号位于最后一行之后的行的开始处时,代码也会错误地(取消)注释该行.这会修复它.*/
        /*所选内容不止一行且插入符号位于行的开头*/
        if (startLine != endLine && stc->GetColumn(stc->GetSelectionEnd()) == 0)
        {
            /*不要考虑插入符号所在的行,因为它不包含任何选择(caret_column==0)...*/
            --endLine;
        }

        /*如果所选内容只有一行...*/
        if (startLine == endLine)
        {
            /*... 然后在行的开头和结尾插入流注释标记*/
            stc->InsertText(stc->PositionFromLine(curLine), comment.streamCommentStart);
            stc->InsertText(stc->GetLineEndPosition(curLine), comment.streamCommentEnd);
        } else {
            /*所选内容不止一行插入框注释开始标记*/
            stc->InsertText(stc->PositionFromLine(curLine), comment.boxCommentStart);
            /*我们已经在上面的9行中注释了第一行*/
            ++curLine;
            while (curLine <= endLine) {
                /*为每行:注释*/
                stc->InsertText(stc->PositionFromLine(curLine), comment.boxCommentMid);
                ++curLine;
            } // end while

            /*插入框注释结束标记并添加新行字符*/
            stc->InsertText(stc->PositionFromLine(curLine), comment.boxCommentEnd + GetEOLStr(stc->GetEOLMode()));
        } // end if
        stc->SetSelectionVoid(stc->PositionFromLine(startLine), stc->PositionFromLine(endLine) + stc->LineLength(endLine));
    }
    stc->EndUndoAction();
}

void MainFrame::OnEditShowCallTip(cb_unused wxCommandEvent &event) {
    CodeBlocksEvent evt(cbEVT_SHOW_CALL_TIP);
    Manager::Get()->ProcessEvent(evt);
}

void MainFrame::OnEditCompleteCode(cb_unused wxCommandEvent &event) {
    CodeBlocksEvent evt(cbEVT_COMPLETE_CODE);
    Manager::Get()->ProcessEvent(evt);
}

void MainFrame::OnEditHighlightMode(wxCommandEvent &event) {
    cbEditor *ed = Manager::Get()->GetEditorManager()->GetBuiltinActiveEditor();
    if (!ed) { return; }

    EditorColourSet *colour_set = Manager::Get()->GetEditorManager()->GetColourSet();
    if (!colour_set) { return; }

    HighlightLanguage lang = colour_set->GetHighlightLanguage(_T(""));
    if (event.GetId() != idEditHighlightModeText) {
        wxMenu *hl = nullptr;
        GetMenuBar()->FindItem(idEditHighlightModeText, &hl);
        if (hl) {
            wxMenuItem *item = hl->FindItem(event.GetId());
            if (item)
#if wxCHECK_VERSION(3, 0, 0)
                lang = colour_set->GetHighlightLanguage(item->GetItemLabelText());
#else
            lang = colour_set->GetHighlightLanguage(item->GetLabel());
#endif
        }
    }
    if (m_pHighlightButton)
        changeButtonLabel(*m_pHighlightButton, colour_set->GetLanguageName(lang));
    ed->SetLanguage(lang);
    Manager::Get()->GetCCManager()->NotifyPluginStatus();
}

void MainFrame::OnEditFoldAll(cb_unused wxCommandEvent &event) {
    cbEditor *ed = Manager::Get()->GetEditorManager()->GetBuiltinActiveEditor();
    if (ed) { ed->FoldAll(); }
}

void MainFrame::OnEditUnfoldAll(cb_unused wxCommandEvent &event) {
    cbEditor *ed = Manager::Get()->GetEditorManager()->GetBuiltinActiveEditor();
    if (ed) { ed->UnfoldAll(); }
}

void MainFrame::OnEditToggleAllFolds(cb_unused wxCommandEvent &event) {
    cbEditor *ed = Manager::Get()->GetEditorManager()->GetBuiltinActiveEditor();
    if (ed) { ed->ToggleAllFolds(); }
}

void MainFrame::OnEditFoldBlock(cb_unused wxCommandEvent &event) {
    cbEditor *ed = Manager::Get()->GetEditorManager()->GetBuiltinActiveEditor();
    if (ed) { ed->FoldBlockFromLine(); }
}

void MainFrame::OnEditUnfoldBlock(cb_unused wxCommandEvent &event) {
    cbEditor *ed = Manager::Get()->GetEditorManager()->GetBuiltinActiveEditor();
    if (ed) { ed->UnfoldBlockFromLine(); }
}

void MainFrame::OnEditToggleFoldBlock(cb_unused wxCommandEvent &event) {
    cbEditor *ed = Manager::Get()->GetEditorManager()->GetBuiltinActiveEditor();
    if (ed) { ed->ToggleFoldBlockFromLine(); }
}

void MainFrame::OnEditEOLMode(wxCommandEvent &event) {
    cbEditor *ed = Manager::Get()->GetEditorManager()->GetBuiltinActiveEditor();
    if (ed) {
        int mode = -1;

        if (event.GetId() == idEditEOLCRLF) { mode = wxSCI_EOL_CRLF; }
        else if (event.GetId() == idEditEOLCR) { mode = wxSCI_EOL_CR; }
        else if (event.GetId() == idEditEOLLF) { mode = wxSCI_EOL_LF; }

        if (mode != -1 && mode != ed->GetControl()->GetEOLMode()) {
            ed->GetControl()->BeginUndoAction();
            ed->GetControl()->ConvertEOLs(mode);
            ed->GetControl()->SetEOLMode(mode);
            ed->GetControl()->EndUndoAction();
        }
    }
}

void MainFrame::OnEditEncoding(wxCommandEvent &event) {
    cbEditor *ed = Manager::Get()->GetEditorManager()->GetBuiltinActiveEditor();
    if (!ed) { return; }

    if (event.GetId() == idEditEncodingUseBom) {
        ed->SetUseBom(!ed->GetUseBom());
        return;
    }

    wxFontEncoding encoding = wxFONTENCODING_SYSTEM;

    if (event.GetId() == idEditEncodingDefault) { encoding = wxFONTENCODING_SYSTEM; }
    else if (event.GetId() == idEditEncodingAscii) { encoding = wxFONTENCODING_ISO8859_1; }
    else if (event.GetId() == idEditEncodingUtf7) { encoding = wxFONTENCODING_UTF7; }
    else if (event.GetId() == idEditEncodingUtf8) { encoding = wxFONTENCODING_UTF8; }
    else if (event.GetId() == idEditEncodingUtf16) { encoding = wxFONTENCODING_UTF16; }
    else if (event.GetId() == idEditEncodingUtf32) { encoding = wxFONTENCODING_UTF32; }
    else if (event.GetId() == idEditEncodingUnicode) { encoding = wxFONTENCODING_UNICODE; }
    else if (event.GetId() == idEditEncodingUnicode16BE) { encoding = wxFONTENCODING_UTF16BE; }
    else if (event.GetId() == idEditEncodingUnicode16LE) { encoding = wxFONTENCODING_UTF16LE; }
    else if (event.GetId() == idEditEncodingUnicode32BE) { encoding = wxFONTENCODING_UTF32BE; }
    else if (event.GetId() == idEditEncodingUnicode32LE) { encoding = wxFONTENCODING_UTF32LE; }

    ed->SetEncoding(encoding);
}

void MainFrame::OnViewLayout(wxCommandEvent &event) {
    LoadViewLayout(m_PluginIDsMap[event.GetId()]);
}

void MainFrame::OnViewLayoutSave(cb_unused wxCommandEvent &event) {
    wxString def = m_LastLayoutName;
    if (def.empty()) { def = Manager::Get()->GetConfigManager(_T("app"))->Read(_T("/main_frame/layout/default")); }
    wxString name = cbGetTextFromUser(wxT("输入此透视图的名称"), wxT("保存当前透视图"), def);
    if (!name.IsEmpty()) {
        DoFixToolbarsLayout();
        SaveViewLayout(name,
                       m_LayoutManager.SavePerspective(),
                       m_pInfoPane->SaveTabOrder(),
                       true);
    }
}

void MainFrame::OnViewLayoutDelete(cb_unused wxCommandEvent &event) {
    if (m_LastLayoutName == gDefaultLayout) {
        if (cbMessageBox(wxT("无法删除默认透视图,但它始终可以还原为预定义状态,是否立即还原?"),
                         wxT("确认"),
                         wxICON_QUESTION | wxYES_NO | wxNO_DEFAULT) == wxID_YES) {
            m_LayoutViews[gDefaultLayout] = gDefaultLayoutData;
            m_LayoutMessagePane[gDefaultLayout] = gDefaultMessagePaneLayoutData;
            LoadViewLayout(gDefaultLayout);
        }
        return;
    }

    if (m_LastLayoutName == gMinimalLayout) {
        if (cbMessageBox(wxT("无法删除最小布局,但它始终可以还原为预定义状态,是否立即还原?"),
                         wxT("确认"),
                         wxICON_QUESTION | wxYES_NO | wxNO_DEFAULT) == wxID_YES) {
            wxString tempLayout = m_PreviousLayoutName;
            m_LayoutViews[gMinimalLayout] = gMinimalLayoutData;
            m_LayoutMessagePane[gMinimalLayout] = gMinimalMessagePaneLayoutData;
            LoadViewLayout(gMinimalLayout);
            m_PreviousLayoutName = tempLayout;
        }
        return;
    }

    if (cbMessageBox(wxString::Format(wxT("是否确实要删除透视图'%s'?"), m_LastLayoutName.wx_str()),
                     wxT("确认"),
                     wxICON_QUESTION | wxYES_NO | wxNO_DEFAULT) == wxID_YES) {
        /*首先从哈希图(hashmap)中删除它*/
        LayoutViewsMap::iterator it = m_LayoutViews.find(m_LastLayoutName);
        if (it != m_LayoutViews.end()) { m_LayoutViews.erase(it); }
        it = m_LayoutMessagePane.find(m_LastLayoutName);
        if (it != m_LayoutMessagePane.end()) { m_LayoutMessagePane.erase(it); }

        /*现在也删除菜单项*/
        wxMenu *viewLayouts = nullptr;
        GetMenuBar()->FindItem(idViewLayoutSave, &viewLayouts);
        if (viewLayouts) {
            int id = viewLayouts->FindItem(m_LastLayoutName);
            if (id != wxNOT_FOUND) { viewLayouts->Delete(id); }
            /*也从映射表(map)上删除id*/
            PluginIDsMap::iterator it2 = m_PluginIDsMap.find(id);
            if (it2 != m_PluginIDsMap.end()) { m_PluginIDsMap.erase(it2); }
        }

        cbMessageBox(wxString::Format(wxT("已删除透视图%s,现在将还原为透视图%s..."), m_LastLayoutName.wx_str(), gDefaultLayout.wx_str()),
                     wxT("信息"), wxICON_INFORMATION);
        /*最后,还原为默认布局,不要求保存旧布局:)*/
        m_LastLayoutName = gDefaultLayout;
        LoadViewLayout(gDefaultLayout);
    }
}

void MainFrame::OnNotebookDoubleClick(cb_unused CodeBlocksEvent &event) {
    if (m_LastLayoutName == gMinimalLayout) { LoadViewLayout(m_PreviousLayoutName.IsEmpty() ? Manager::Get()->GetConfigManager(_T("app"))->Read(_T("/environment/view/layout_to_toggle"), gDefaultLayout) : m_PreviousLayoutName); }
    else {
        ConfigManager *cfg = Manager::Get()->GetConfigManager(_T("app"));
        if (cfg->ReadBool(_T("/environment/view/dbl_clk_maximize"), true)) { LoadViewLayout(gMinimalLayout); }
    }
}

void MainFrame::OnViewScriptConsole(cb_unused wxCommandEvent &event) {
    ShowHideScriptConsole();
}

void MainFrame::OnViewHideEditorTabs(cb_unused wxCommandEvent &event) {
    cbAuiNotebook *nb = Manager::Get()->GetEditorManager()->GetNotebook();
    if (nb) {
        bool hide_editor_tabs = nb->GetTabCtrlHeight() > 0;

        if (hide_editor_tabs) { nb->SetTabCtrlHeight(0); }
        else { nb->SetTabCtrlHeight(-1); }

        Manager::Get()->GetConfigManager(_T("app"))->Write(_T("/environment/hide_editor_tabs"), hide_editor_tabs);
    }
}

void MainFrame::OnSearchFind(wxCommandEvent &event) {
    bool bDoMultipleFiles = (event.GetId() == idSearchFindInFiles);
    if (!bDoMultipleFiles) { bDoMultipleFiles = !Manager::Get()->GetEditorManager()->GetBuiltinActiveEditor(); }
    m_findReplace.ShowFindDialog(false, bDoMultipleFiles);
}

void MainFrame::OnSearchFindNext(wxCommandEvent &event) {
    bool bNext = !(event.GetId() == idSearchFindPrevious);
    m_findReplace.FindNext(bNext, nullptr, nullptr, false);
}

void MainFrame::OnSearchFindNextSelected(wxCommandEvent &event) {
    bool bNext = !(event.GetId() == idSearchFindSelectedPrevious);
    m_findReplace.FindSelectedText(bNext);
}

void MainFrame::OnSearchReplace(wxCommandEvent &event) {
    bool bDoMultipleFiles = (event.GetId() == idSearchReplaceInFiles);
    if (!bDoMultipleFiles)
        bDoMultipleFiles = !Manager::Get()->GetEditorManager()->GetBuiltinActiveEditor();
    m_findReplace.ShowFindDialog(true, bDoMultipleFiles);
}

void MainFrame::OnSearchGotoLine(cb_unused wxCommandEvent &event) {
    cbEditor *ed = Manager::Get()->GetEditorManager()->GetBuiltinActiveEditor();
    if (!ed) { return; }

    int max = ed->GetControl()->LineFromPosition(ed->GetControl()->GetLength()) + 1;

    /**
    @备注 我们使用wxGetText而不是wxGetNumber,因为wxGetNumber*必须*提供一个初始行号...这没有意义,只是让用户在每次实例化对话框时都删除初始行号.
     但是,这只是一个临时的使用,因为使用的默认对话框也不太合适.
    */
    wxString strLine = cbGetTextFromUser(wxString::Format(wxT("行 (1 - %d): "), max),
                                         wxT("转到行"),
                                         wxT(""),
                                         this);
    long int line = 0;
    strLine.ToLong(&line);
    if (line >= 1 && line <= max) {
        ed->UnfoldBlockFromLine(line - 1);
        ed->GotoLine(line - 1);
    }
}

void MainFrame::OnSearchGotoNextChanged(cb_unused wxCommandEvent &event) {
    cbEditor *ed = Manager::Get()->GetEditorManager()->GetBuiltinActiveEditor();
    if (ed) { ed->GotoNextChanged(); }
}

void MainFrame::OnSearchGotoPrevChanged(cb_unused wxCommandEvent &event) {
    cbEditor *ed = Manager::Get()->GetEditorManager()->GetBuiltinActiveEditor();
    if (ed) { ed->GotoPreviousChanged(); }
}

void MainFrame::OnHelpAbout(wxCommandEvent & WXUNUSED(event)) {
    dlgAbout dlg(this);
    PlaceWindow(&dlg, pdlHead);
    dlg.ShowModal();
}

void MainFrame::OnHelpTips(cb_unused wxCommandEvent &event) {
    ShowTips(true);
}

void MainFrame::OnFileMenuUpdateUI(wxUpdateUIEvent &event) {
    if (Manager::IsAppShuttingDown()) {
        event.Skip();
        return;
    }

    if (!ProjectManager::CanShutdown() || !EditorManager::CanShutdown()) {
        event.Enable(false);
        return;
    }

    EditorManager *editorManager = Manager::Get()->GetEditorManager();
    EditorBase *ed = (editorManager ? editorManager->GetActiveEditor() : nullptr);
    EditorBase *sh = (editorManager ? editorManager->GetEditor(g_StartHereTitle) : nullptr);

    const int id = event.GetId();

    /*与单个文件相关的菜单项*/
    if (id == idFileClose || id == idFileCloseAll || id == idFileSaveAs) { event.Enable(ed && ed != sh); }
    else if (id == idFileSave) { event.Enable(ed && ed->GetModified()); }
    else if (id == idFilePrint) { event.Enable(editorManager && editorManager->GetBuiltinActiveEditor()); }
    else if (id == idFileOpen) { event.Enable(true); }
    else {
        ProjectManager *projectManager = Manager::Get()->GetProjectManager();
        cbProject *project = (projectManager ? projectManager->GetActiveProject() : nullptr);
        if (project && project->GetCurrentlyCompilingTarget()) {
            event.Enable(false);
            return;
        }

        /*项目相关菜单项*/
        if (id == idFileReopenProject) { event.Enable(true); }
        else if (id == idFileCloseProject || id == idFileSaveProjectAs || id == idFileSaveProjectTemplate) { event.Enable(project != nullptr); }
        else if (id == idFileSaveProject) { event.Enable(project && project->GetModified()); }
        else if (id == idFileOpenDefWorkspace || id == idFileSaveWorkspaceAs || id == idFileSaveAll) { event.Enable(true); }
        else {
            /*与工作区相关的菜单项*/
            const cbWorkspace *workspace = Manager::Get()->GetProjectManager()->GetWorkspace();

            if (id == idFileSaveWorkspace) { event.Enable(workspace && workspace->GetModified()); }
            else if (id == idFileCloseWorkspace) { event.Enable(workspace != nullptr); }
            else { event.Skip(); }
        }
    }
}

/*更新编辑菜单栏界面*/
void MainFrame::OnEditMenuUpdateUI(wxUpdateUIEvent &event) {
    if (Manager::IsAppShuttingDown()) {
        event.Skip();
        return;
    }

    cbEditor *ed = NULL;
    EditorBase *eb = NULL;
    bool hasSel = false;
    bool canUndo = false;
    bool canRedo = false;
    bool canPaste = false;
    bool canCut = false;
    bool canSelAll = false;

    if (Manager::Get()->GetEditorManager() && !Manager::IsAppShuttingDown()) {
        ed = Manager::Get()->GetEditorManager()->GetBuiltinActiveEditor();
        eb = Manager::Get()->GetEditorManager()->GetActiveEditor();
    }

    wxMenuBar *mbar = GetMenuBar();

    if (eb) {
        canUndo = eb->CanUndo();
        canRedo = eb->CanRedo();
        hasSel = eb->HasSelection();
        canPaste = eb->CanPaste();
        canCut = !eb->IsReadOnly() && hasSel;
        canSelAll = eb->CanSelectAll();
    }

    mbar->Enable(idEditUndo, canUndo);
    mbar->Enable(idEditRedo, canRedo);
    mbar->Enable(idEditClearHistory, canUndo || canRedo);
    mbar->Enable(idEditCut, canCut);
    mbar->Enable(idEditCopy, hasSel);
    mbar->Enable(idEditPaste, canPaste);
    mbar->Enable(idEditSwapHeaderSource, ed);
    mbar->Enable(idEditGotoMatchingBrace, ed);
    mbar->Enable(idEditHighlightMode, ed);
    mbar->Enable(idEditSelectAll, canSelAll);
    mbar->Enable(idEditSelectNext, ed);
    mbar->Enable(idEditSelectNextSkip, hasSel);
    mbar->Enable(idEditBookmarks, ed);
    mbar->Enable(idEditFolding, ed &&
                                Manager::Get()->GetConfigManager(_T("editor"))->ReadBool(_T("/folding/show_folds"), false));
    mbar->Enable(idEditEOLMode, ed);
    mbar->Enable(idEditEncoding, ed);
    mbar->Enable(idEditSpecialCommands, ed);
    mbar->Enable(idEditSpecialCommandsCase, ed && hasSel);
    mbar->Enable(idEditCommentSelected, ed);
    mbar->Enable(idEditUncommentSelected, ed);
    mbar->Enable(idEditToggleCommentSelected, ed);
    mbar->Enable(idEditStreamCommentSelected, ed);
    mbar->Enable(idEditBoxCommentSelected, ed);
    mbar->Enable(idEditShowCallTip, ed);
    mbar->Enable(idEditCompleteCode, ed);

    if (ed) {
        /*
        哦...这是计算机程序历史上最奇怪/最愚蠢/最令人沮丧的错误...
        在wxGTK下,如果你试图检查[Check()]一个菜单项(如果它的容器菜单被禁用),你会进入一个无休止的消息循环,消耗100%的CPU...
        该死的!这修复了可怕的"linux挂起关闭项目(linux-hang-on-close-project)"错误.
        */

/*
 switch (ed->GetControl()->GetEOLMode()) {
            case wxSCI_EOL_CRLF:
                mbar->Check(idEditEOLCRLF, true);
                break;
            case wxSCI_EOL_CR:
                mbar->Check(idEditEOLCR, true);
                break;
            case wxSCI_EOL_LF:
                mbar->Check(idEditEOLLF, true);
                break;
            default:
                (platform::windows ? mbar->Check(idEditEOLCRLF, true) : mbar->Check(idEditEOLLF, true));
                break;
        }
        */

        int eolm = ed->GetControl()->GetEOLMode();
        if (eolm == wxSCI_EOL_CRLF) {
            if (idEditEOLCRLF) { mbar->Check(idEditEOLCRLF, true); }
        } else if (eolm == wxSCI_EOL_CR) {
            if (idEditEOLCR) { mbar->Check(idEditEOLCR, true); }
        } else if (eolm == wxSCI_EOL_LF) {
            if (idEditEOLLF) { mbar->Check(idEditEOLLF, true); }
        } else {
            if (platform::windows) {
                if (idEditEOLCRLF) { mbar->Check(idEditEOLCRLF, true); }
            } else {
                if (idEditEOLLF) { mbar->Check(idEditEOLLF, true); }
            }
        }

        bool defenc = ed && (ed->GetEncoding() == wxFONTENCODING_SYSTEM || ed->GetEncoding() == wxLocale::GetSystemEncoding());

        if (idEditEncodingDefault) { mbar->Check(idEditEncodingDefault, defenc); }
        if (idEditEncodingUseBom) { mbar->Check(idEditEncodingUseBom, ed && ed->GetUseBom()); }
        if (idEditEncodingAscii) { mbar->Check(idEditEncodingAscii, ed && ed->GetEncoding() == wxFONTENCODING_ISO8859_1); }
        if (idEditEncodingUtf7) { mbar->Check(idEditEncodingUtf7, ed && ed->GetEncoding() == wxFONTENCODING_UTF7); }
        if (idEditEncodingUtf8) { mbar->Check(idEditEncodingUtf8, ed && ed->GetEncoding() == wxFONTENCODING_UTF8); }
        if (idEditEncodingUnicode) { mbar->Check(idEditEncodingUnicode, ed && ed->GetEncoding() == wxFONTENCODING_UNICODE); }
        if (idEditEncodingUtf16) { mbar->Check(idEditEncodingUtf16, ed && ed->GetEncoding() == wxFONTENCODING_UTF16); }
        if (idEditEncodingUtf32) { mbar->Check(idEditEncodingUtf32, ed && ed->GetEncoding() == wxFONTENCODING_UTF32); }
        if (idEditEncodingUnicode16BE) { mbar->Check(idEditEncodingUnicode16BE, ed && ed->GetEncoding() == wxFONTENCODING_UTF16BE); }
        if (idEditEncodingUnicode16LE) { mbar->Check(idEditEncodingUnicode16LE, ed && ed->GetEncoding() == wxFONTENCODING_UTF16LE); }
        if (idEditEncodingUnicode32BE) { mbar->Check(idEditEncodingUnicode32BE, ed && ed->GetEncoding() == wxFONTENCODING_UTF32BE); }
        if (idEditEncodingUnicode32LE) { mbar->Check(idEditEncodingUnicode32LE, ed && ed->GetEncoding() == wxFONTENCODING_UTF32LE); }

        wxMenu *hl = nullptr;
        mbar->FindItem(idEditHighlightModeText, &hl);
        if (hl) {
            EditorColourSet *colour_set = ed->GetColourSet();
            if (colour_set) {
                int mi = hl->FindItem(colour_set->GetLanguageName(ed->GetLanguage()));
                if (mi) { mbar->Check(mi, true); }
            }
        }
    }

    if (m_pToolbar) {
        m_pToolbar->EnableTool(idEditUndo, canUndo);
        m_pToolbar->EnableTool(idEditRedo, canRedo);
        m_pToolbar->EnableTool(idEditCut, canCut);
        m_pToolbar->EnableTool(idEditCopy, hasSel);
        m_pToolbar->EnableTool(idEditPaste, canPaste);
    }

    event.Skip();
}

void MainFrame::OnViewMenuUpdateUI(wxUpdateUIEvent &event) {
    if (Manager::IsAppShuttingDown()) {
        event.Skip();
        return;
    }

    wxMenuBar *mbar = GetMenuBar();
    cbEditor *ed = Manager::Get()->GetEditorManager() ? Manager::Get()->GetEditorManager()->GetBuiltinActiveEditor() : nullptr;
    bool manVis = m_LayoutManager.GetPane(m_pPrjManUI->GetNotebook()).IsShown();

    mbar->Check(idViewManager, manVis);
    mbar->Check(idViewLogManager, m_LayoutManager.GetPane(m_pInfoPane).IsShown());
    mbar->Check(idViewStartPage, Manager::Get()->GetEditorManager()->GetEditor(g_StartHereTitle) != NULL);
    mbar->Check(idViewStatusbar, GetStatusBar() && GetStatusBar()->IsShown());
    mbar->Check(idViewScriptConsole, m_LayoutManager.GetPane(m_pScriptConsole).IsShown());
    mbar->Check(idViewHideEditorTabs, Manager::Get()->GetEditorManager()->GetNotebook()->GetTabCtrlHeight() == 0);
    mbar->Check(idViewFullScreen, IsFullScreen());

    mbar->Enable(idViewFocusEditor, ed);
    mbar->Enable(idViewFocusManagement, manVis);
    mbar->Enable(idViewFocusLogsAndOthers, m_pInfoPane->IsShown());

    /*工具栏*/
    wxMenu *viewToolbars = nullptr;
    GetMenuBar()->FindItem(idViewToolMain, &viewToolbars);
    if (viewToolbars) {
        SetChecksForViewToolbarsMenu(*viewToolbars);
    }

    event.Skip();
}

void MainFrame::OnSearchMenuUpdateUI(wxUpdateUIEvent &event) {
    if (Manager::IsAppShuttingDown()) {
        event.Skip();
        return;
    }

    cbEditor *ed = Manager::Get()->GetEditorManager()
                   ? Manager::Get()->GetEditorManager()->GetBuiltinEditor(
                    Manager::Get()->GetEditorManager()->GetActiveEditor()) : nullptr;

    bool enableGoto = false;
    if (ed) { enableGoto = Manager::Get()->GetConfigManager(_T("editor"))->ReadBool(_T("/margin/use_changebar"), true) && (ed->CanUndo() || ed->CanRedo()); }

    wxMenuBar *mbar = GetMenuBar();

    /*"查找"和"替换"始终在文件中启用(查找|替换)(idSearchFindInFiles and idSearchReplaceInFiles)*/

    mbar->Enable(idSearchFind, ed);
    mbar->Enable(idSearchFindNext, ed);
    mbar->Enable(idSearchFindPrevious, ed);
    mbar->Enable(idSearchFindSelectedNext, ed);
    mbar->Enable(idSearchFindSelectedPrevious, ed);
    mbar->Enable(idSearchReplace, ed);
    mbar->Enable(idSearchGotoLine, ed);
    mbar->Enable(idSearchGotoNextChanged, enableGoto);
    mbar->Enable(idSearchGotoPreviousChanged, enableGoto);

    event.Skip();
}


void MainFrame::OnEditorUpdateUI(CodeBlocksEvent &event) {
    if (Manager::IsAppShuttingDown()) {
        event.Skip();
        return;
    }

    if (Manager::Get()->GetEditorManager() && event.GetEditor() == Manager::Get()->GetEditorManager()->GetActiveEditor())
        DoUpdateStatusBar();

    event.Skip();
}

namespace {
    /*工具条适配信息结构*/
    struct ToolbarFitInfo {
        int row;
        wxRect rect;
        wxWindow *window;

        bool operator<(const ToolbarFitInfo &r) const {
            if (row < r.row) { return true; }
            else if (row == r.row) { return rect.x < r.rect.x; }
            else { return false; }
        }
    };

    /*采集工具条*/
    static void CollectToolbars(std::set<ToolbarFitInfo> &result, wxAuiManager &layoutManager) {
        const wxAuiPaneInfoArray &panes = layoutManager.GetAllPanes();
        for (size_t ii = 0; ii < panes.GetCount(); ++ii) {
            const wxAuiPaneInfo &info = panes[ii];
            if (info.IsToolbar() && info.IsShown()) {
                ToolbarFitInfo f;
                f.row = info.dock_row;
                f.rect = info.rect;
                f.window = info.window;
                result.insert(f);
            }
        }
    }
    /*工具条每行(某部分性能)信息结构*/
    struct ToolbarRowInfo {
        ToolbarRowInfo() {}

        ToolbarRowInfo(int width_, int position_) : width(width_), position(position_) {}

        int width, position;
    };

    /*使所有工具栏可见的函数(自动匹配).*/
    static void FitToolbars(wxAuiManager &layoutManager, wxWindow *mainFrame) {
        std::set<ToolbarFitInfo> sorted;
        CollectToolbars(sorted, layoutManager);
        if (sorted.empty()) { return; }

        int maxWidth = mainFrame->GetSize().x;
        int gripperSize = layoutManager.GetArtProvider()->GetMetric(wxAUI_DOCKART_GRIPPER_SIZE);

        /*将所有工具栏尽可能向左移动,并将不适合(布局)项添加到列表中*/
        std::vector<ToolbarRowInfo> rows;
        std::vector<wxWindow *> nonFitingToolbars;
        for (std::set<ToolbarFitInfo>::const_iterator it = sorted.begin(); it != sorted.end(); ++it) {
            wxAuiPaneInfo &pane = layoutManager.GetPane(it->window);
            int row = pane.dock_row;
            while (static_cast<int>(rows.size()) <= row) { rows.push_back(ToolbarRowInfo(0, 0)); }

            int maxX = rows[row].width + it->window->GetBestSize().x + gripperSize;
            if (maxX > maxWidth) { nonFitingToolbars.push_back(it->window); }
            else {
                rows[row].width = maxX;
                pane.Position(rows[row].position++);
            }
        }

        /*移动底部的不适合的工具栏*/
        int lastRow = rows.empty() ? 0 : (rows.size() - 1);
        int position = rows.back().position, maxX = rows.back().width;
        for (std::vector<wxWindow *>::iterator it = nonFitingToolbars.begin(); it != nonFitingToolbars.end(); ++it) {
            maxX += (*it)->GetBestSize().x;
            maxX += gripperSize;
            if (maxX > maxWidth) {
                position = 0;
                lastRow++;
                maxX = (*it)->GetBestSize().x + gripperSize;
            }
            layoutManager.GetPane(*it).Position(position++).Row(lastRow);
        }
    }

    /*函数,该函数尝试最小化工具栏使用的空间.它还可以用来显示已经超出窗口的工具栏.*/
    static void OptimizeToolbars(wxAuiManager &layoutManager, wxWindow *mainFrame) {
        std::set<ToolbarFitInfo> sorted;
        CollectToolbars(sorted, layoutManager);
        if (sorted.empty()) { return; }

        int maxWidth = mainFrame->GetSize().x;
        int lastRow = 0, position = 0, maxX = 0;
        int gripperSize = layoutManager.GetArtProvider()->GetMetric(wxAUI_DOCKART_GRIPPER_SIZE);

        for (std::set<ToolbarFitInfo>::const_iterator it = sorted.begin(); it != sorted.end(); ++it) {
            maxX += it->window->GetBestSize().x;
            maxX += gripperSize;
            if (maxX > maxWidth) {
                position = 0;
                lastRow++;
                maxX = it->window->GetBestSize().x + gripperSize;
            }
            layoutManager.GetPane(it->window).Position(position++).Row(lastRow);
        }
    }

} // anomymous namespace

void MainFrame::OnViewToolbarsFit(cb_unused wxCommandEvent &event) {
    FitToolbars(m_LayoutManager, this);
    DoUpdateLayout();
}

void MainFrame::OnViewToolbarsOptimize(cb_unused wxCommandEvent &event) {
    OptimizeToolbars(m_LayoutManager, this);
    DoUpdateLayout();
}

void MainFrame::OnToggleBar(wxCommandEvent &event) {
    wxWindow *win = nullptr;
    bool toolbar = false;
    if (event.GetId() == idViewManager) { win = m_pPrjManUI->GetNotebook(); }
    else if (event.GetId() == idViewLogManager) { win = m_pInfoPane; }
    else if (event.GetId() == idViewToolMain) {
        win = m_pToolbar;
        toolbar = true;
    } else if (event.GetId() == idViewToolDebugger) {
        win = m_debuggerToolbarHandler->GetToolbar();
        toolbar = true;
    } else {
        wxString pluginName = m_PluginIDsMap[event.GetId()];
        if (!pluginName.IsEmpty()) {
            cbPlugin *plugin = Manager::Get()->GetPluginManager()->FindPluginByName(pluginName);
            if (plugin) {
                win = m_PluginsTools[plugin];
                toolbar = true;
            }
        }
    }

    if (win) {
        /*使用最后可见的大小作为最佳大小,日志和其他不再"忘记"它的大小*/
        if (!event.IsChecked()) { m_LayoutManager.GetPane(win).BestSize(win->GetSize()); }

        m_LayoutManager.GetPane(win).Show(event.IsChecked());
        if (toolbar) { FitToolbars(m_LayoutManager, this); }
        DoUpdateLayout();
    }
}

void MainFrame::OnToggleStatusBar(cb_unused wxCommandEvent &event) {
    cbStatusBar *sb = (cbStatusBar *) GetStatusBar();
    if (!sb) {return;}

    ConfigManager *cfg = Manager::Get()->GetConfigManager(_T("app"));
    const bool show = !cfg->ReadBool(_T("/main_frame/statusbar"), true);
    cfg->Write(_T("/main_frame/statusbar"), show);

    DoUpdateStatusBar();
    sb->Show(show);
    if (show) {SendSizeEvent();}
    DoUpdateLayout();
}

void MainFrame::OnFocusEditor(cb_unused wxCommandEvent &event) {
    EditorManager *edman = Manager::Get()->GetEditorManager();
    cbAuiNotebook *nb = edman ? edman->GetNotebook() : nullptr;
    if (nb) { nb->FocusActiveTabCtrl(); }
}

void MainFrame::OnFocusManagement(cb_unused wxCommandEvent &event) {
    cbAuiNotebook *nb = m_pPrjManUI ? m_pPrjManUI->GetNotebook() : nullptr;
    if (nb) { nb->FocusActiveTabCtrl(); }
}

void MainFrame::OnFocusLogsAndOthers(cb_unused wxCommandEvent &event) {
    if (m_pInfoPane) { m_pInfoPane->FocusActiveTabCtrl(); }
}

/*切换编辑器选项卡*/
void MainFrame::OnSwitchTabs(cb_unused wxCommandEvent &event) {
    /*从编辑器管理器(editormanager)获取标签页:*/
    cbAuiNotebook *nb = Manager::Get()->GetEditorManager()->GetNotebook();
    if (!nb) { return; }
    /*创建容器并添加所有打开的编辑器*/
    wxSwitcherItems items;
    items.AddGroup(wxT("打开的文件"), wxT("editors"));
    if (!Manager::Get()->GetConfigManager(_T("app"))->ReadBool(_T("/environment/tabs_stacked_based_switching"))) {
        /*按选项卡顺序切换选项卡编辑器*/
        for (size_t i = 0; i < nb->GetPageCount(); ++i) {
            wxString title = nb->GetPageText(i);
            wxWindow *window = nb->GetPage(i);
            items.AddItem(title, title, GetEditorDescription(static_cast<EditorBase *> (window)), i, nb->GetPageBitmap(i)).SetWindow(window);
        }

        /*选择聚焦(被设定为焦点)编辑器*/
        int idx = items.GetIndexForFocus();
        if (idx != wxNOT_FOUND) { items.SetSelection(idx); }
    } else {
        /*使用上次使用的顺序切换选项卡编辑器*/
        int index = 0;
        cbNotebookStack *body;
        for (body = Manager::Get()->GetEditorManager()->GetNotebookStack(); body != NULL; body = body->next) {
            index = nb->GetPageIndex(body->window);
            if (index == wxNOT_FOUND) { continue; }
            wxString title = nb->GetPageText(index);
            items.AddItem(title, title, GetEditorDescription(static_cast<EditorBase *> (body->window)), index, nb->GetPageBitmap(index)).SetWindow(body->window);
        }

        /*选择聚焦(被设定为焦点)编辑器*/
        if (items.GetItemCount() > 2) {
            /*CTRL+TAB直接选择最后一个编辑器,而不是当前编辑器*/
            items.SetSelection(2);
        } else { items.SetSelection(items.GetItemCount() - 1); }
    }

    /*创建切换器对话框*/
    wxSwitcherDialog dlg(items, wxGetApp().GetTopWindow());

    /*非windows平台的Ctrl+Tab解决方案:*/
    if (platform::cocoa) { dlg.SetModifierKey(WXK_ALT); }
    else if (platform::gtk) { dlg.SetExtraNavigationKey(wxT(',')); }

    /*最后显示对话框:*/
    int answer = dlg.ShowModal();

    /*如有必要,请更改选定的编辑器:*/
    if ((answer == wxID_OK) && (dlg.GetSelection() != -1)) {
        wxSwitcherItem &item = items.GetItem(dlg.GetSelection());
        wxWindow *win = item.GetWindow();
        if (win) {
            nb->SetSelection(item.GetId());
            win->SetFocus();
        }
    }
}

void MainFrame::OnToggleStartPage(cb_unused wxCommandEvent &event) {

    int toggle = -1;
    if (Manager::Get()->GetEditorManager()->GetEditor(g_StartHereTitle) == NULL) {
        toggle = 1;
    }
    ShowHideStartPage(false, toggle);
}

void MainFrame::OnToggleFullScreen(cb_unused wxCommandEvent &event) {
    ShowFullScreen(!IsFullScreen(), wxFULLSCREEN_NOTOOLBAR// | wxFULLSCREEN_NOSTATUSBAR
                                    | wxFULLSCREEN_NOBORDER | wxFULLSCREEN_NOCAPTION);

    /*创建全屏关闭按钮(如果我们处于全屏状态)*/
    if (IsFullScreen()) {
        /*显示容器右下角的按钮*/
        wxSize containerSize = GetClientSize();
        wxSize buttonSize = m_pCloseFullScreenBtn->GetSize();
        /*对齐*/
        m_pCloseFullScreenBtn->Move(containerSize.GetWidth() - buttonSize.GetWidth(),
                                    containerSize.GetHeight() - buttonSize.GetHeight());

        m_pCloseFullScreenBtn->Show(true);
        m_pCloseFullScreenBtn->Raise();
    } else { m_pCloseFullScreenBtn->Show(false); }
}

void MainFrame::OnPluginInstalled(CodeBlocksEvent &event) {
    PluginsUpdated(event.GetPlugin(), Installed);
}

void MainFrame::OnPluginUninstalled(CodeBlocksEvent &event) {
    if (Manager::IsAppShuttingDown()) { return; }
    PluginsUpdated(event.GetPlugin(), Uninstalled);
}

void MainFrame::OnPluginLoaded(CodeBlocksEvent &event) {
    cbPlugin *plug = event.GetPlugin();
    if (plug) {
        DoAddPlugin(plug);
        const PluginInfo *info = Manager::Get()->GetPluginManager()->GetPluginInfo(plug);
        wxString msg = info ? info->title : wxT("<未知插件>");
        Manager::Get()->GetLogManager()->DebugLog(wxString::Format(wxT("%s插件被激活"), msg.wx_str()));
    }
}

void MainFrame::OnPluginUnloaded(CodeBlocksEvent &event) {
    if (Manager::IsAppShuttingDown()) { return; }

    cbPlugin *plugin = event.GetPlugin();

    cbStatusBar *sb = (cbStatusBar *) GetStatusBar();
    if (sb) { sb->RemoveField(plugin); }

    /*删除工具栏(如果有)*/
    if (m_PluginsTools[plugin]) {
        /*在销毁工具栏之前断开鼠标右键单击事件处理程序的连接*/
        bool result = m_PluginsTools[plugin]->Disconnect(wxID_ANY, wxEVT_COMMAND_TOOL_RCLICKED, wxCommandEventHandler(MainFrame::OnToolBarRightClick));
        cbAssert(result);
        m_LayoutManager.DetachPane(m_PluginsTools[plugin]);
        m_PluginsTools[plugin]->Destroy();
        m_PluginsTools.erase(plugin);
        DoUpdateLayout();
    }

    PluginsUpdated(plugin, Unloaded);
}

void MainFrame::OnSettingsEnvironment(cb_unused wxCommandEvent &event) {
    bool tbarsmall = m_SmallToolBar;
    bool needRestart = false;

    EnvironmentSettingsDlg dlg(this, m_LayoutManager.GetArtProvider());
    PlaceWindow(&dlg);
    if (dlg.ShowModal() == wxID_OK) {
        DoUpdateEditorStyle();
        DoUpdateLayoutColours();

        m_SmallToolBar = Manager::Get()->GetConfigManager(_T("app"))->ReadBool(_T("/environment/toolbar_size"), true);
        needRestart = m_SmallToolBar != tbarsmall;
        Manager::Get()->GetLogManager()->NotifyUpdate();
        Manager::Get()->GetEditorManager()->RecreateOpenEditorStyles();
        Manager::Get()->GetCCManager()->UpdateEnvSettings();
        m_pPrjManUI->RebuildTree();
        ShowHideStartPage();

        CodeBlocksEvent event2(cbEVT_SETTINGS_CHANGED);
        event2.SetInt(cbSettingsType::Environment);
        Manager::Get()->ProcessEvent(event2);
    }
    if (needRestart) { cbMessageBox(wxT("需要重新启动Code::Blocks才能使更改生效."), wxT("信息"), wxICON_INFORMATION); }
}

void MainFrame::OnGlobalUserVars(cb_unused wxCommandEvent &event) {
    Manager::Get()->GetUserVariableManager()->Configure();
}

void MainFrame::OnSettingsEditor(cb_unused wxCommandEvent &event) {
    /*编辑器词法分析器(lexers)加载需要一些时间,最好用(时间)沙漏鼠标指针反映这一点*/
    wxBeginBusyCursor();

    EditorConfigurationDlg dlg(Manager::Get()->GetAppWindow());
    PlaceWindow(&dlg);

    /*完成,还原指针*/
    wxEndBusyCursor();

    if (dlg.ShowModal() == wxID_OK) {
        Manager::Get()->GetEditorManager()->RecreateOpenEditorStyles();

        CodeBlocksEvent event2(cbEVT_SETTINGS_CHANGED);
        event2.SetInt(cbSettingsType::Editor);
        Manager::Get()->ProcessEvent(event2);
    }
}

void MainFrame::OnSettingsCompiler(cb_unused wxCommandEvent &event) {
    CompilerSettingsDlg dlg(this);
    PlaceWindow(&dlg);
    if (dlg.ShowModal() == wxID_OK) {
        CodeBlocksEvent event2(cbEVT_SETTINGS_CHANGED);
        event2.SetInt(cbSettingsType::Compiler);
        Manager::Get()->ProcessEvent(event2);
    }
}

void MainFrame::OnSettingsDebugger(cb_unused wxCommandEvent &event) {
    DebuggerSettingsDlg dlg(this);
    PlaceWindow(&dlg);
    if (dlg.ShowModal() == wxID_OK) {
        CodeBlocksEvent event2(cbEVT_SETTINGS_CHANGED);
        event2.SetInt(cbSettingsType::Debugger);
        Manager::Get()->ProcessEvent(event2);
    }
}

void MainFrame::OnSettingsPlugins(cb_unused wxCommandEvent &event) {
    if (Manager::Get()->GetPluginManager()->Configure() == wxID_OK) {
        CodeBlocksEvent event2(cbEVT_SETTINGS_CHANGED);
        event2.SetInt(cbSettingsType::Plugins);
        Manager::Get()->ProcessEvent(event2);
    }
}

void MainFrame::OnSettingsScripting(cb_unused wxCommandEvent &event) {
    ScriptingSettingsDlg dlg(this);
    if (dlg.ShowModal() == wxID_OK) { RunStartupScripts(); }
}

void MainFrame::OnProjectActivated(CodeBlocksEvent &event) {
    DoUpdateAppTitle();
    event.Skip();
}

void MainFrame::OnProjectOpened(CodeBlocksEvent &event) {
    ShowHideStartPage(true);
    event.Skip();
}

void MainFrame::OnEditorOpened(CodeBlocksEvent &event) {
    DoUpdateAppTitle();
    event.Skip();
}

void MainFrame::OnEditorActivated(CodeBlocksEvent &event) {
    DoUpdateAppTitle();
    DoUpdateStatusBar();

    EditorBase *editor = event.GetEditor();
    if (editor && editor->IsBuiltinEditor()) {
        ConfigManager *cfgEditor = Manager::Get()->GetConfigManager(_T("editor"));
        if (cfgEditor->ReadBool(_T("/sync_editor_with_project_manager"), false)) {
            ProjectFile *pf = static_cast<cbEditor *>(editor)->GetProjectFile();
            if (pf) { m_pPrjManUI->ShowFileInTree(*pf); }
        }
    }

    event.Skip();
}

void MainFrame::OnEditorClosed(CodeBlocksEvent &event) {
    DoUpdateAppTitle();
    DoUpdateStatusBar();
    event.Skip();
}

void MainFrame::OnEditorSaved(CodeBlocksEvent &event) {
    DoUpdateAppTitle();
    event.Skip();
}

void MainFrame::OnEditorModified(CodeBlocksEvent &event) {
    DoUpdateAppTitle();
    event.Skip();
}

void MainFrame::OnProjectClosed(CodeBlocksEvent &event) {
    ShowHideStartPage();
    event.Skip();
}

void MainFrame::OnPageChanged(wxNotebookEvent &event) {
    DoUpdateAppTitle();
    event.Skip();
}

void MainFrame::OnShiftTab(cb_unused wxCommandEvent &event) {
    /*必须确保它是cbEditor而不是EditorBase*/
    cbEditor *ed = Manager::Get()->GetEditorManager()->GetBuiltinActiveEditor();
    if (ed) { ed->DoUnIndent(); }
}

void MainFrame::OnCtrlAltTab(cb_unused wxCommandEvent &event) {
    wxCommandEvent dummy;
    switch (m_LastCtrlAltTabWindow) {
        case 1:
            /*焦点在管理面板->循环到编辑器*/
            m_LastCtrlAltTabWindow = 2;
            OnFocusEditor(dummy);
            break;
        case 2:
            /*焦点在编辑器->循环到日志和其他*/
            m_LastCtrlAltTabWindow = 3;
            OnFocusLogsAndOthers(dummy);
            break;
        case 3:
        default:
        /*焦点是在日志和其他标签 -> 落空,焦点循环到管理面板上*/
            m_LastCtrlAltTabWindow = 1;
            OnFocusManagement(dummy);
    }
}

void MainFrame::OnRequestDockWindow(CodeBlocksDockEvent &event) {
    if (Manager::IsAppShuttingDown()) { return; }

    wxAuiPaneInfo info;
    wxString name = event.name;
    if (name.IsEmpty()) {
        static int idx = 0;
        name = wxString::Format(wxT("UntitledPane%d"), ++idx);
    }

    /*TODO (mandrav##):检查具有相同名称的现有面板*/
    info.Name(name);
    info.Caption(event.title.IsEmpty() ? name : event.title);
    switch (event.dockSide) {
        case CodeBlocksDockEvent::dsLeft:
            info.Left();
            break;
        case CodeBlocksDockEvent::dsRight:
            info.Right();
            break;
        case CodeBlocksDockEvent::dsTop:
            info.Top();
            break;
        case CodeBlocksDockEvent::dsBottom:
            info.Bottom();
            break;
        case CodeBlocksDockEvent::dsFloating:
            info.Float();
            break;
        case CodeBlocksDockEvent::dsUndefined: // fall through
        default:
            break;
    }
    info.Show(event.shown);
    info.BestSize(event.desiredSize);
    info.FloatingSize(event.floatingSize);
    info.FloatingPosition(event.floatingPos);
    info.MinSize(event.minimumSize);
    info.Layer(event.stretch ? 1 : 0);

    if (event.row != -1) { info.Row(event.row); }
    if (event.column != -1) { info.Position(event.column); }
    info.CloseButton(event.hideable ? true : false);
    m_LayoutManager.AddPane(event.pWindow, info);
    DoUpdateLayout();
}

void MainFrame::OnRequestUndockWindow(CodeBlocksDockEvent &event) {
    wxAuiPaneInfo info = m_LayoutManager.GetPane(event.pWindow);
    if (info.IsOk()) {
        m_LayoutManager.DetachPane(event.pWindow);
        DoUpdateLayout();
    }
}

void MainFrame::OnRequestShowDockWindow(CodeBlocksDockEvent &event) {
    m_LayoutManager.GetPane(event.pWindow).Show();
    DoUpdateLayout();

    CodeBlocksDockEvent evt(cbEVT_DOCK_WINDOW_VISIBILITY);
    evt.pWindow = event.pWindow;
    Manager::Get()->ProcessEvent(evt);
}

void MainFrame::OnRequestHideDockWindow(CodeBlocksDockEvent &event) {
    m_LayoutManager.GetPane(event.pWindow).Hide();
    DoUpdateLayout();

    CodeBlocksDockEvent evt(cbEVT_DOCK_WINDOW_VISIBILITY);
    evt.pWindow = event.pWindow;
    Manager::Get()->ProcessEvent(evt);
}

void MainFrame::OnDockWindowVisibility(cb_unused CodeBlocksDockEvent &event) {
    /*if (m_ScriptConsoleID != -1 && event.GetId() == m_ScriptConsoleID) { ShowHideScriptConsole(); }*/
}

void MainFrame::OnLayoutUpdate(cb_unused CodeBlocksLayoutEvent &event) {
    DoFixToolbarsLayout();
    DoUpdateLayout();
}

void MainFrame::OnLayoutQuery(CodeBlocksLayoutEvent &event) {
    event.layout = !m_LastLayoutName.IsEmpty() ? m_LastLayoutName : gDefaultLayout;
    event.StopPropagation();
}

void MainFrame::OnLayoutSwitch(CodeBlocksLayoutEvent &event) {
    LoadViewLayout(event.layout, true);
}

void MainFrame::OnAddLogWindow(CodeBlocksLogEvent &event) {
    if (Manager::IsAppShuttingDown()) { return; }
    wxWindow *p = event.window;
    if (p) { m_pInfoPane->AddNonLogger(p, event.title, event.icon); }
    else {
        p = event.logger->CreateControl(m_pInfoPane);
        if (p) { m_pInfoPane->AddLogger(event.logger, p, event.title, event.icon); }
    }
    Manager::Get()->GetLogManager()->NotifyUpdate();
}

void MainFrame::OnRemoveLogWindow(CodeBlocksLogEvent &event) {
    if (Manager::IsAppShuttingDown()) { return; }
    if (event.window) {
        m_pInfoPane->RemoveNonLogger(event.window);
    }else {
        m_pInfoPane->DeleteLogger(event.logger);
    }
}

void MainFrame::OnHideLogWindow(CodeBlocksLogEvent &event) {
    if (event.window) { m_pInfoPane->HideNonLogger(event.window); }
    else if (event.logger) { m_pInfoPane->Hide(event.logger); }
}

void MainFrame::OnSwitchToLogWindow(CodeBlocksLogEvent &event) {
    if (event.window) { m_pInfoPane->ShowNonLogger(event.window); }
    else if (event.logger) { m_pInfoPane->Show(event.logger); }
}

void MainFrame::OnGetActiveLogWindow(CodeBlocksLogEvent &event) {
    bool is_logger;
    int page_index = m_pInfoPane->GetCurrentPage(is_logger);

    event.logger = nullptr;
    event.window = nullptr;

    if (is_logger) { event.logger = m_pInfoPane->GetLogger(page_index); }
    else { event.window = m_pInfoPane->GetWindow(page_index); }
}

void MainFrame::OnShowLogManager(cb_unused CodeBlocksLogEvent &event) {
    if (!Manager::Get()->GetConfigManager(_T("message_manager"))->ReadBool(_T("/auto_hide"), false)) { return; }

    m_LayoutManager.GetPane(m_pInfoPane).Show(true);
    DoUpdateLayout();
}

void MainFrame::OnHideLogManager(cb_unused CodeBlocksLogEvent &event) {
    if (!Manager::Get()->GetConfigManager(_T("message_manager"))->ReadBool(_T("/auto_hide"), false) ||
        m_AutoHideLockCounter > 0) { return; }

    m_LayoutManager.GetPane(m_pInfoPane).Show(false);
    DoUpdateLayout();
}

void MainFrame::OnLockLogManager(cb_unused CodeBlocksLogEvent &event) {
    if (!Manager::Get()->GetConfigManager(_T("message_manager"))->ReadBool(_T("/auto_hide"), false)) { return; }
    ++m_AutoHideLockCounter;
}

void MainFrame::OnUnlockLogManager(cb_unused CodeBlocksLogEvent &event) {
    if (!Manager::Get()->GetConfigManager(_T("message_manager"))->ReadBool(_T("/auto_hide"), false) &&
        m_AutoHideLockCounter > 0) { return; }
    if (--m_AutoHideLockCounter == 0) {
        m_LayoutManager.GetPane(m_pInfoPane).Show(false);
        DoUpdateLayout();
    }
}

/*响应状态栏的高亮显示*/
void MainFrame::OnHighlightMenu(cb_unused wxCommandEvent &event) {
    cbEditor *ed = Manager::Get()->GetEditorManager()->GetBuiltinActiveEditor();
    if (!ed) { return; }

    EditorColourSet *colour_set = Manager::Get()->GetEditorManager()->GetColourSet();
    if (!colour_set) { return; }

    wxMenu *hl = nullptr;
    GetMenuBar()->FindItem(idEditHighlightModeText, &hl);
    wxArrayString langs = colour_set->GetAllHighlightLanguages();

    wxMenu mm;
    mm.AppendRadioItem(idEditHighlightModeText, wxT("纯文本"), wxT("将当前文档的突出显示模式切换为纯文本"));
    Connect(hl->FindItem(wxT("纯文本")), -1, wxEVT_COMMAND_MENU_SELECTED,(wxObjectEventFunction) (wxEventFunction) (wxCommandEventFunction) &MainFrame::OnEditHighlightMode);
    for (size_t i = 0; i < langs.GetCount(); ++i) {
        /*每20个项目分成列*/
        if (i > 0 && !(i % 20)) {
            mm.Break();
        }
        mm.AppendRadioItem(hl->FindItem(langs[i]), langs[i], wxString::Format(wxT("将当前文档的突出显示模式切换到%s"), langs[i].wx_str()));
        Connect(hl->FindItem(langs[i]), -1, wxEVT_COMMAND_MENU_SELECTED,(wxObjectEventFunction) (wxEventFunction) (wxCommandEventFunction) &MainFrame::OnEditHighlightMode);
    }
    int checkeditem = -1;
    checkeditem = hl->FindItem(colour_set->GetLanguageName(ed->GetLanguage()));
    if (checkeditem != wxNOT_FOUND) { mm.Check(checkeditem, true); }

    wxRect rect;
    GetStatusBar()->GetFieldRect(1, rect);
    PopupMenu(&mm, GetStatusBar()->GetPosition() + rect.GetPosition());
}

void MainFrame::StartupDone() {
    Manager::Get()->GetLogManager()->Log(wxT("程序框架启动完成,开始刷新界面布局..."));
    m_StartupDone = true;
    DoUpdateLayout();
}

wxStatusBar *MainFrame::OnCreateStatusBar(int number, long style, wxWindowID id, const wxString &name) {
    cbStatusBar *sb = new cbStatusBar(this, id, style, name);
    sb->SetFieldsCount(number);

    return sb;
}

/*让用户从上下文菜单切换工具栏*/
void MainFrame::OnMouseRightUp(wxMouseEvent &event) {
    PopupToggleToolbarMenu();
    event.Skip();
}

void MainFrame::OnToolBarRightClick(wxCommandEvent &event) {
    PopupToggleToolbarMenu();
    event.Skip();
}

void MainFrame::PopupToggleToolbarMenu() {
    wxMenuBar *menuBar = Manager::Get()->GetAppFrame()->GetMenuBar();
    int idx = menuBar->FindMenu(wxT("视图"));
    if (idx == wxNOT_FOUND) { return; }
    wxMenu *viewMenu = menuBar->GetMenu(idx);
    idx = viewMenu->FindItem(wxT("工具栏"));
    if (idx == wxNOT_FOUND) { return; }

    /*克隆"视图"->"工具栏"菜单并将其显示为弹出式菜单.*/
    wxMenu *toolbarMenu = viewMenu->FindItem(idx)->GetSubMenu();
    wxMenu menu;
    for (size_t ii = 0; ii < toolbarMenu->GetMenuItemCount(); ++ii) {
        wxMenuItem *old = toolbarMenu->FindItemByPosition(ii);
        if (!old) { continue; }
        wxMenuItem *item;
        item = new wxMenuItem(&menu, old->GetId(), old->GetItemLabelText(), old->GetHelp(), old->GetKind());
        menu.Append(item);
    }
    SetChecksForViewToolbarsMenu(menu);
    PopupMenu(&menu);
}

void MainFrame::SetChecksForViewToolbarsMenu(wxMenu &menu) {
    for (size_t i = 0; i < menu.GetMenuItemCount(); ++i) {
        wxMenuItem *item = menu.GetMenuItems()[i];
        wxString pluginName = m_PluginIDsMap[item->GetId()];
        if (!pluginName.IsEmpty()) {
            cbPlugin *plugin = Manager::Get()->GetPluginManager()->FindPluginByName(pluginName);
            if (plugin && item) {
                item->Check(m_LayoutManager.GetPane(m_PluginsTools[plugin]).IsShown());
            }
        }
    }

    if(idViewToolMain){menu.Check(idViewToolMain, m_LayoutManager.GetPane(m_pToolbar).IsShown());}
    if(idViewToolDebugger){menu.Check(idViewToolDebugger, m_LayoutManager.GetPane(m_debuggerToolbarHandler->GetToolbar(false)).IsShown());}
}
